mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-23 12:13:48 +01:00
Update to arkworks libraries (#3)
Co-authored-by: Nicholas Ward <npward@berkeley.edu>
This commit is contained in:
579
src/fields/cubic_extension.rs
Normal file
579
src/fields/cubic_extension.rs
Normal file
@@ -0,0 +1,579 @@
|
||||
use ark_ff::{
|
||||
fields::{CubicExtField, CubicExtParameters, Field},
|
||||
Zero,
|
||||
};
|
||||
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use crate::{
|
||||
fields::{fp::FpVar, FieldOpsBounds, FieldVar},
|
||||
prelude::*,
|
||||
ToConstraintFieldGadget, Vec,
|
||||
};
|
||||
|
||||
/// This struct is the `R1CS` equivalent of the cubic extension field type
|
||||
/// in `ark-ff`, i.e. `ark_ff::CubicExtField`.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))]
|
||||
#[must_use]
|
||||
pub struct CubicExtVar<BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>>
|
||||
where
|
||||
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||
{
|
||||
/// The zero-th coefficient of this field element.
|
||||
pub c0: BF,
|
||||
/// The first coefficient of this field element.
|
||||
pub c1: BF,
|
||||
/// The second coefficient of this field element.
|
||||
pub c2: BF,
|
||||
#[derivative(Debug = "ignore")]
|
||||
_params: PhantomData<P>,
|
||||
}
|
||||
|
||||
/// This trait describes parameters that are used to implement arithmetic for
|
||||
/// `CubicExtVar`.
|
||||
pub trait CubicExtVarParams<BF: FieldVar<Self::BaseField, Self::BasePrimeField>>:
|
||||
CubicExtParameters
|
||||
where
|
||||
for<'a> &'a BF: FieldOpsBounds<'a, Self::BaseField, BF>,
|
||||
{
|
||||
/// Multiply the base field of the `CubicExtVar` by the appropriate
|
||||
/// Frobenius coefficient. This is equivalent to
|
||||
/// `Self::mul_base_field_by_frob_coeff(c1, c2, power)`.
|
||||
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>,
|
||||
{
|
||||
/// Constructs a `CubicExtVar` from the underlying coefficients.
|
||||
#[inline]
|
||||
pub fn new(c0: BF, c1: BF, c2: BF) -> Self {
|
||||
let _params = PhantomData;
|
||||
Self {
|
||||
c0,
|
||||
c1,
|
||||
c2,
|
||||
_params,
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplies a variable of the base field by the cubic nonresidue
|
||||
/// `P::NONRESIDUE` that is used to construct the extension field.
|
||||
#[inline]
|
||||
pub fn mul_base_field_by_nonresidue(fe: &BF) -> Result<BF, SynthesisError> {
|
||||
Ok(fe * P::NONRESIDUE)
|
||||
}
|
||||
|
||||
/// Multiplies `self` by a constant from the base field.
|
||||
#[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)
|
||||
}
|
||||
|
||||
/// Sets `self = self.mul_by_base_field_constant(fe)`.
|
||||
#[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) -> 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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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))
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn inverse(&self) -> Result<Self, SynthesisError> {
|
||||
let mode = if self.is_constant() {
|
||||
AllocationMode::Constant
|
||||
} else {
|
||||
AllocationMode::Witness
|
||||
};
|
||||
let inverse = Self::new_variable(
|
||||
self.cs(),
|
||||
|| {
|
||||
self.value()
|
||||
.map(|f| f.inverse().unwrap_or(CubicExtField::zero()))
|
||||
},
|
||||
mode,
|
||||
)?;
|
||||
self.mul_equals(&inverse, &Self::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>,
|
||||
{
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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>,
|
||||
{
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn to_bits_le(&self) -> Result<Vec<Boolean<P::BasePrimeField>>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bits_le()?;
|
||||
let mut c1 = self.c1.to_bits_le()?;
|
||||
let mut c2 = self.c2.to_bits_le()?;
|
||||
c0.append(&mut c1);
|
||||
c0.append(&mut c2);
|
||||
Ok(c0)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<P::BasePrimeField>>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_non_unique_bits_le()?;
|
||||
let mut c1 = self.c1.to_non_unique_bits_le()?;
|
||||
let mut c2 = self.c2.to_non_unique_bits_le()?;
|
||||
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>,
|
||||
{
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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> ToConstraintFieldGadget<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>,
|
||||
BF: ToConstraintFieldGadget<P::BasePrimeField>,
|
||||
{
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn to_constraint_field(&self) -> Result<Vec<FpVar<P::BasePrimeField>>, SynthesisError> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
res.extend_from_slice(&self.c0.to_constraint_field()?);
|
||||
res.extend_from_slice(&self.c1.to_constraint_field()?);
|
||||
res.extend_from_slice(&self.c2.to_constraint_field()?);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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>;
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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>;
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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(ark_relations::ns!(cs, "c0"), || c0, mode)?;
|
||||
let c1 = BF::new_variable(ark_relations::ns!(cs, "c1"), || c1, mode)?;
|
||||
let c2 = BF::new_variable(ark_relations::ns!(cs, "c2"), || c2, mode)?;
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
}
|
||||
247
src/fields/fp/cmp.rs
Normal file
247
src/fields/fp/cmp.rs
Normal file
@@ -0,0 +1,247 @@
|
||||
use crate::{
|
||||
boolean::Boolean,
|
||||
fields::{fp::FpVar, FieldVar},
|
||||
prelude::*,
|
||||
ToBitsGadget,
|
||||
};
|
||||
use ark_ff::PrimeField;
|
||||
use ark_relations::r1cs::{SynthesisError, Variable};
|
||||
use core::cmp::Ordering;
|
||||
|
||||
impl<F: PrimeField> FpVar<F> {
|
||||
/// This function enforces the ordering between `self` and `other`. The
|
||||
/// constraint system will not be satisfied otherwise. If `self` should
|
||||
/// also be checked for equality, e.g. `self <= other` instead of `self <
|
||||
/// other`, set `should_also_check_quality` to `true`. This variant
|
||||
/// verifies `self` and `other` are `<= (p-1)/2`.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn enforce_cmp(
|
||||
&self,
|
||||
other: &FpVar<F>,
|
||||
ordering: Ordering,
|
||||
should_also_check_equality: bool,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
|
||||
left.enforce_smaller_than(&right)
|
||||
}
|
||||
|
||||
/// This function enforces the ordering between `self` and `other`. The
|
||||
/// constraint system will not be satisfied otherwise. If `self` should
|
||||
/// also be checked for equality, e.g. `self <= other` instead of `self <
|
||||
/// other`, set `should_also_check_quality` to `true`. This variant
|
||||
/// assumes `self` and `other` are `<= (p-1)/2` and does not generate
|
||||
/// constraints to verify that.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn enforce_cmp_unchecked(
|
||||
&self,
|
||||
other: &FpVar<F>,
|
||||
ordering: Ordering,
|
||||
should_also_check_equality: bool,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
|
||||
left.enforce_smaller_than_unchecked(&right)
|
||||
}
|
||||
|
||||
/// This function checks the ordering between `self` and `other`. It outputs
|
||||
/// self `Boolean` that contains the result - `1` if true, `0`
|
||||
/// otherwise. The constraint system will be satisfied in any case. If
|
||||
/// `self` should also be checked for equality, e.g. `self <= other`
|
||||
/// instead of `self < other`, set `should_also_check_quality` to
|
||||
/// `true`. This variant verifies `self` and `other` are `<= (p-1)/2`.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn is_cmp(
|
||||
&self,
|
||||
other: &FpVar<F>,
|
||||
ordering: Ordering,
|
||||
should_also_check_equality: bool,
|
||||
) -> Result<Boolean<F>, SynthesisError> {
|
||||
let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
|
||||
left.is_smaller_than(&right)
|
||||
}
|
||||
|
||||
/// This function checks the ordering between `self` and `other`. It outputs
|
||||
/// a `Boolean` that contains the result - `1` if true, `0` otherwise.
|
||||
/// The constraint system will be satisfied in any case. If `self`
|
||||
/// should also be checked for equality, e.g. `self <= other` instead of
|
||||
/// `self < other`, set `should_also_check_quality` to `true`. This
|
||||
/// variant assumes `self` and `other` are `<= (p-1)/2` and does not
|
||||
/// generate constraints to verify that.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn is_cmp_unchecked(
|
||||
&self,
|
||||
other: &FpVar<F>,
|
||||
ordering: Ordering,
|
||||
should_also_check_equality: bool,
|
||||
) -> Result<Boolean<F>, SynthesisError> {
|
||||
let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
|
||||
left.is_smaller_than_unchecked(&right)
|
||||
}
|
||||
|
||||
fn process_cmp_inputs(
|
||||
&self,
|
||||
other: &Self,
|
||||
ordering: Ordering,
|
||||
should_also_check_equality: bool,
|
||||
) -> Result<(Self, Self), SynthesisError> {
|
||||
let (left, right) = match ordering {
|
||||
Ordering::Less => (self, other),
|
||||
Ordering::Greater => (other, self),
|
||||
Ordering::Equal => Err(SynthesisError::Unsatisfiable)?,
|
||||
};
|
||||
let right_for_check = if should_also_check_equality {
|
||||
right + F::one()
|
||||
} else {
|
||||
right.clone()
|
||||
};
|
||||
|
||||
Ok((left.clone(), right_for_check))
|
||||
}
|
||||
|
||||
/// Helper function to enforce that `self <= (p-1)/2`.
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
pub fn enforce_smaller_or_equal_than_mod_minus_one_div_two(
|
||||
&self,
|
||||
) -> Result<(), SynthesisError> {
|
||||
// It's okay to use `to_non_unique_bits` bits here because we're enforcing
|
||||
// self <= (p-1)/2, which implies self < p.
|
||||
let _ = Boolean::enforce_smaller_or_equal_than_le(
|
||||
&self.to_non_unique_bits_le()?,
|
||||
F::modulus_minus_one_div_two(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function to check `self < other` and output a result bit. This
|
||||
/// function verifies `self` and `other` are `<= (p-1)/2`.
|
||||
fn is_smaller_than(&self, other: &FpVar<F>) -> Result<Boolean<F>, SynthesisError> {
|
||||
self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
|
||||
other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
|
||||
self.is_smaller_than_unchecked(other)
|
||||
}
|
||||
|
||||
/// Helper function to check `self < other` and output a result bit. This
|
||||
/// function assumes `self` and `other` are `<= (p-1)/2` and does not
|
||||
/// generate constraints to verify that.
|
||||
fn is_smaller_than_unchecked(&self, other: &FpVar<F>) -> Result<Boolean<F>, SynthesisError> {
|
||||
Ok((self - other)
|
||||
.double()?
|
||||
.to_bits_le()?
|
||||
.first()
|
||||
.unwrap()
|
||||
.clone())
|
||||
}
|
||||
|
||||
/// Helper function to enforce `self < other`. This function verifies `self`
|
||||
/// and `other` are `<= (p-1)/2`.
|
||||
fn enforce_smaller_than(&self, other: &FpVar<F>) -> Result<(), SynthesisError> {
|
||||
self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
|
||||
other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
|
||||
self.enforce_smaller_than_unchecked(other)
|
||||
}
|
||||
|
||||
/// Helper function to enforce `self < other`. This function assumes `self`
|
||||
/// and `other` are `<= (p-1)/2` and does not generate constraints to
|
||||
/// verify that.
|
||||
fn enforce_smaller_than_unchecked(&self, other: &FpVar<F>) -> Result<(), SynthesisError> {
|
||||
let is_smaller_than = self.is_smaller_than_unchecked(other)?;
|
||||
let lc_one = lc!() + Variable::One;
|
||||
[self, other]
|
||||
.cs()
|
||||
.enforce_constraint(is_smaller_than.lc(), lc_one.clone(), lc_one)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::{alloc::AllocVar, fields::fp::FpVar};
|
||||
use ark_ff::{PrimeField, UniformRand};
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_test_curves::bls12_381::Fr;
|
||||
|
||||
#[test]
|
||||
fn test_cmp() {
|
||||
let mut rng = &mut XorShiftRng::from_seed([
|
||||
0x5d, 0xbe, 0x62, 0x59, 0x8d, 0x31, 0x3d, 0x76, 0x32, 0x37, 0xdb, 0x17, 0xe5, 0xbc,
|
||||
0x06, 0x54,
|
||||
]);
|
||||
fn rand_in_range<R: Rng>(rng: &mut R) -> Fr {
|
||||
let pminusonedivtwo: Fr = Fr::modulus_minus_one_div_two().into();
|
||||
let mut r;
|
||||
loop {
|
||||
r = Fr::rand(rng);
|
||||
if r <= pminusonedivtwo {
|
||||
break;
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
for i in 0..10 {
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
let a = rand_in_range(&mut rng);
|
||||
let a_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(a)).unwrap();
|
||||
let b = rand_in_range(&mut rng);
|
||||
let b_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(b)).unwrap();
|
||||
|
||||
match a.cmp(&b) {
|
||||
Ordering::Less => {
|
||||
a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap();
|
||||
a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap();
|
||||
}
|
||||
Ordering::Greater => {
|
||||
a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap();
|
||||
a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
println!("number of constraints: {}", cs.num_constraints());
|
||||
}
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
println!("Finished with satisfaction tests");
|
||||
|
||||
for _i in 0..10 {
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
let a = rand_in_range(&mut rng);
|
||||
let a_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(a)).unwrap();
|
||||
let b = rand_in_range(&mut rng);
|
||||
let b_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(b)).unwrap();
|
||||
|
||||
match b.cmp(&a) {
|
||||
Ordering::Less => {
|
||||
a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap();
|
||||
a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap();
|
||||
}
|
||||
Ordering::Greater => {
|
||||
a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap();
|
||||
a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
assert!(!cs.is_satisfied().unwrap());
|
||||
}
|
||||
|
||||
for _i in 0..10 {
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
let a = rand_in_range(&mut rng);
|
||||
let a_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(a)).unwrap();
|
||||
a_var.enforce_cmp(&a_var, Ordering::Less, false).unwrap();
|
||||
|
||||
assert!(!cs.is_satisfied().unwrap());
|
||||
}
|
||||
|
||||
for _i in 0..10 {
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
let a = rand_in_range(&mut rng);
|
||||
let a_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(a)).unwrap();
|
||||
a_var.enforce_cmp(&a_var, Ordering::Less, true).unwrap();
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
1015
src/fields/fp/mod.rs
Normal file
1015
src/fields/fp/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
171
src/fields/fp12.rs
Normal file
171
src/fields/fp12.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use crate::fields::{fp2::Fp2Var, fp6_3over2::Fp6Var, quadratic_extension::*, FieldVar};
|
||||
use ark_ff::fields::{fp12_2over3over2::*, fp6_3over2::Fp6Parameters, Field, QuadExtParameters};
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
|
||||
/// A degree-12 extension field constructed as the tower of a
|
||||
/// quadratic extension over a cubic extension over a quadratic extension field.
|
||||
/// This is the R1CS equivalent of `ark_ff::fp12_2over3over2::Fp12<P>`.
|
||||
pub type Fp12Var<P> = QuadExtVar<Fp6Var<<P as Fp12Parameters>::Fp6Params>, Fp12ParamsWrapper<P>>;
|
||||
|
||||
type Fp2Params<P> = <<P as Fp12Parameters>::Fp6Params as Fp6Parameters>::Fp2Params;
|
||||
|
||||
impl<P: Fp12Parameters> QuadExtVarParams<Fp6Var<P::Fp6Params>> for Fp12ParamsWrapper<P> {
|
||||
fn mul_base_field_var_by_frob_coeff(fe: &mut Fp6Var<P::Fp6Params>, power: usize) {
|
||||
fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
fe.c2 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp12Parameters> Fp12Var<P> {
|
||||
/// Multiplies by a sparse element of the form `(c0 = (c0, c1, 0), c1 = (0,
|
||||
/// d1, 0))`.
|
||||
#[inline]
|
||||
pub fn mul_by_014(
|
||||
&self,
|
||||
c0: &Fp2Var<Fp2Params<P>>,
|
||||
c1: &Fp2Var<Fp2Params<P>>,
|
||||
d1: &Fp2Var<Fp2Params<P>>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let v0 = self.c0.mul_by_c0_c1_0(&c0, &c1)?;
|
||||
let v1 = self.c1.mul_by_0_c1_0(&d1)?;
|
||||
let new_c0 = Self::mul_base_field_by_nonresidue(&v1)? + &v0;
|
||||
|
||||
let new_c1 = (&self.c0 + &self.c1).mul_by_c0_c1_0(&c0, &(c1 + d1))? - &v0 - &v1;
|
||||
Ok(Self::new(new_c0, new_c1))
|
||||
}
|
||||
|
||||
/// Multiplies by a sparse element of the form `(c0 = (c0, 0, 0), c1 = (d0,
|
||||
/// d1, 0))`.
|
||||
#[inline]
|
||||
pub fn mul_by_034(
|
||||
&self,
|
||||
c0: &Fp2Var<Fp2Params<P>>,
|
||||
d0: &Fp2Var<Fp2Params<P>>,
|
||||
d1: &Fp2Var<Fp2Params<P>>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let a0 = &self.c0.c0 * c0;
|
||||
let a1 = &self.c0.c1 * c0;
|
||||
let a2 = &self.c0.c2 * c0;
|
||||
let a = Fp6Var::new(a0, a1, a2);
|
||||
let b = self.c1.mul_by_c0_c1_0(&d0, &d1)?;
|
||||
|
||||
let c0 = c0 + d0;
|
||||
let c1 = d1;
|
||||
let e = (&self.c0 + &self.c1).mul_by_c0_c1_0(&c0, &c1)?;
|
||||
let new_c1 = e - (&a + &b);
|
||||
let new_c0 = Self::mul_base_field_by_nonresidue(&b)? + &a;
|
||||
|
||||
Ok(Self::new(new_c0, new_c1))
|
||||
}
|
||||
|
||||
/// Squares `self` when `self` is in the cyclotomic subgroup.
|
||||
pub fn cyclotomic_square(&self) -> Result<Self, SynthesisError> {
|
||||
if characteristic_square_mod_6_is_one(Fp12::<P>::characteristic()) {
|
||||
let fp2_nr = <P::Fp6Params as Fp6Parameters>::NONRESIDUE;
|
||||
|
||||
let z0 = &self.c0.c0;
|
||||
let z4 = &self.c0.c1;
|
||||
let z3 = &self.c0.c2;
|
||||
let z2 = &self.c1.c0;
|
||||
let z1 = &self.c1.c1;
|
||||
let z5 = &self.c1.c2;
|
||||
|
||||
// t0 + t1*y = (z0 + z1*y)^2 = a^2
|
||||
let tmp = z0 * z1;
|
||||
let t0 = {
|
||||
let tmp1 = z0 + z1;
|
||||
let tmp2 = z1 * fp2_nr + z0;
|
||||
let tmp4 = &tmp * fp2_nr + &tmp;
|
||||
tmp1 * tmp2 - tmp4
|
||||
};
|
||||
let t1 = tmp.double()?;
|
||||
|
||||
// t2 + t3*y = (z2 + z3*y)^2 = b^2
|
||||
let tmp = z2 * z3;
|
||||
let t2 = {
|
||||
// (z2 + &z3) * &(z2 + &(fp2_nr * &z3)) - &tmp - &(tmp * &fp2_nr);
|
||||
let tmp1 = z2 + z3;
|
||||
let tmp2 = z3 * fp2_nr + z2;
|
||||
let tmp4 = &tmp * fp2_nr + &tmp;
|
||||
tmp1 * tmp2 - tmp4
|
||||
};
|
||||
let t3 = tmp.double()?;
|
||||
|
||||
// t4 + t5*y = (z4 + z5*y)^2 = c^2
|
||||
let tmp = z4 * z5;
|
||||
let t4 = {
|
||||
// (z4 + &z5) * &(z4 + &(fp2_nr * &z5)) - &tmp - &(tmp * &fp2_nr);
|
||||
let tmp1 = z4 + z5;
|
||||
let tmp2 = (z5 * fp2_nr) + z4;
|
||||
let tmp4 = (&tmp * fp2_nr) + &tmp;
|
||||
(tmp1 * tmp2) - tmp4
|
||||
};
|
||||
let t5 = tmp.double()?;
|
||||
|
||||
// for A
|
||||
|
||||
// z0 = 3 * t0 - 2 * z0
|
||||
let c0_c0 = (&t0 - z0).double()? + &t0;
|
||||
|
||||
// z1 = 3 * t1 + 2 * z1
|
||||
let c1_c1 = (&t1 + z1).double()? + &t1;
|
||||
|
||||
// for B
|
||||
|
||||
// z2 = 3 * (xi * t5) + 2 * z2
|
||||
let c1_c0 = {
|
||||
let tmp = &t5 * fp2_nr;
|
||||
(z2 + &tmp).double()? + &tmp
|
||||
};
|
||||
|
||||
// z3 = 3 * t4 - 2 * z3
|
||||
let c0_c2 = (&t4 - z3).double()? + &t4;
|
||||
|
||||
// for C
|
||||
|
||||
// z4 = 3 * t2 - 2 * z4
|
||||
let c0_c1 = (&t2 - z4).double()? + &t2;
|
||||
|
||||
// z5 = 3 * t3 + 2 * z5
|
||||
let c1_c2 = (&t3 + z5).double()? + &t3;
|
||||
let c0 = Fp6Var::new(c0_c0, c0_c1, c0_c2);
|
||||
let c1 = Fp6Var::new(c1_c0, c1_c1, c1_c2);
|
||||
|
||||
Ok(Self::new(c0, c1))
|
||||
} else {
|
||||
self.square()
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `Self::cyclotomic_exp`, but additionally uses cyclotomic squaring.
|
||||
pub fn optimized_cyclotomic_exp(
|
||||
&self,
|
||||
exponent: impl AsRef<[u64]>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
use ark_ff::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 = res.cyclotomic_square()?;
|
||||
}
|
||||
|
||||
if value != 0 {
|
||||
found_nonzero = true;
|
||||
|
||||
if value > 0 {
|
||||
res *= self;
|
||||
} else {
|
||||
res *= &self_inverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
12
src/fields/fp2.rs
Normal file
12
src/fields/fp2.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use crate::fields::{fp::FpVar, quadratic_extension::*};
|
||||
use ark_ff::fields::{Fp2Parameters, Fp2ParamsWrapper, QuadExtParameters};
|
||||
|
||||
/// A quadratic extension field constructed over a prime field.
|
||||
/// This is the R1CS equivalent of `ark_ff::Fp2<P>`.
|
||||
pub type Fp2Var<P> = QuadExtVar<FpVar<<P as Fp2Parameters>::Fp>, Fp2ParamsWrapper<P>>;
|
||||
|
||||
impl<P: Fp2Parameters> QuadExtVarParams<FpVar<P::Fp>> for Fp2ParamsWrapper<P> {
|
||||
fn mul_base_field_var_by_frob_coeff(fe: &mut FpVar<P::Fp>, power: usize) {
|
||||
*fe *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
}
|
||||
}
|
||||
17
src/fields/fp3.rs
Normal file
17
src/fields/fp3.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use crate::fields::{cubic_extension::*, fp::FpVar};
|
||||
use ark_ff::fields::{CubicExtParameters, Fp3Parameters, Fp3ParamsWrapper};
|
||||
|
||||
/// A cubic extension field constructed over a prime field.
|
||||
/// This is the R1CS equivalent of `ark_ff::Fp3<P>`.
|
||||
pub type Fp3Var<P> = CubicExtVar<FpVar<<P as Fp3Parameters>::Fp>, Fp3ParamsWrapper<P>>;
|
||||
|
||||
impl<P: Fp3Parameters> CubicExtVarParams<FpVar<P::Fp>> for Fp3ParamsWrapper<P> {
|
||||
fn mul_base_field_vars_by_frob_coeff(
|
||||
c1: &mut FpVar<P::Fp>,
|
||||
c2: &mut FpVar<P::Fp>,
|
||||
power: usize,
|
||||
) {
|
||||
*c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
*c2 *= Self::FROBENIUS_COEFF_C2[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
}
|
||||
}
|
||||
14
src/fields/fp4.rs
Normal file
14
src/fields/fp4.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::fields::{fp2::Fp2Var, quadratic_extension::*};
|
||||
use ark_ff::fields::{Fp4Parameters, Fp4ParamsWrapper, QuadExtParameters};
|
||||
|
||||
/// A quartic extension field constructed as the tower of a
|
||||
/// quadratic extension over a quadratic extension field.
|
||||
/// This is the R1CS equivalent of `ark_ff::Fp4<P>`.
|
||||
pub type Fp4Var<P> = QuadExtVar<Fp2Var<<P as Fp4Parameters>::Fp2Params>, Fp4ParamsWrapper<P>>;
|
||||
|
||||
impl<P: Fp4Parameters> QuadExtVarParams<Fp2Var<P::Fp2Params>> for Fp4ParamsWrapper<P> {
|
||||
fn mul_base_field_var_by_frob_coeff(fe: &mut Fp2Var<P::Fp2Params>, power: usize) {
|
||||
fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
}
|
||||
}
|
||||
15
src/fields/fp6_2over3.rs
Normal file
15
src/fields/fp6_2over3.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use crate::fields::{fp3::Fp3Var, quadratic_extension::*};
|
||||
use ark_ff::fields::{fp6_2over3::*, QuadExtParameters};
|
||||
|
||||
/// A sextic extension field constructed as the tower of a
|
||||
/// quadratic extension over a cubic extension field.
|
||||
/// This is the R1CS equivalent of `ark_ff::fp6_2over3::Fp6<P>`.
|
||||
pub type Fp6Var<P> = QuadExtVar<Fp3Var<<P as Fp6Parameters>::Fp3Params>, Fp6ParamsWrapper<P>>;
|
||||
|
||||
impl<P: Fp6Parameters> QuadExtVarParams<Fp3Var<P::Fp3Params>> for Fp6ParamsWrapper<P> {
|
||||
fn mul_base_field_var_by_frob_coeff(fe: &mut Fp3Var<P::Fp3Params>, power: usize) {
|
||||
fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
fe.c2 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
}
|
||||
}
|
||||
85
src/fields/fp6_3over2.rs
Normal file
85
src/fields/fp6_3over2.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use crate::fields::{cubic_extension::*, fp2::*};
|
||||
use ark_ff::fields::{fp6_3over2::*, CubicExtParameters, Fp2};
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
use core::ops::MulAssign;
|
||||
|
||||
/// A sextic extension field constructed as the tower of a
|
||||
/// cubic extension over a quadratic extension field.
|
||||
/// This is the R1CS equivalent of `ark_ff::fp6_3over3::Fp6<P>`.
|
||||
pub type Fp6Var<P> = CubicExtVar<Fp2Var<<P as Fp6Parameters>::Fp2Params>, Fp6ParamsWrapper<P>>;
|
||||
|
||||
impl<P: Fp6Parameters> CubicExtVarParams<Fp2Var<P::Fp2Params>> for Fp6ParamsWrapper<P> {
|
||||
fn mul_base_field_vars_by_frob_coeff(
|
||||
c1: &mut Fp2Var<P::Fp2Params>,
|
||||
c2: &mut Fp2Var<P::Fp2Params>,
|
||||
power: usize,
|
||||
) {
|
||||
*c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
*c2 *= Self::FROBENIUS_COEFF_C2[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp6Parameters> Fp6Var<P> {
|
||||
/// Multiplies `self` by a sparse element which has `c0 == c2 == zero`.
|
||||
pub fn mul_by_0_c1_0(&self, c1: &Fp2Var<P::Fp2Params>) -> Result<Self, SynthesisError> {
|
||||
// Karatsuba multiplication
|
||||
// v0 = a0 * b0 = 0
|
||||
|
||||
// v1 = a1 * b1
|
||||
let v1 = &self.c1 * c1;
|
||||
|
||||
// v2 = a2 * b2 = 0
|
||||
|
||||
let a1_plus_a2 = &self.c1 + &self.c2;
|
||||
let b1_plus_b2 = c1.clone();
|
||||
|
||||
let a0_plus_a1 = &self.c0 + &self.c1;
|
||||
|
||||
// c0 = (NONRESIDUE * ((a1 + a2)*(b1 + b2) - v1 - v2)) + v0
|
||||
// = NONRESIDUE * ((a1 + a2) * b1 - v1)
|
||||
let c0 = &(a1_plus_a2 * &b1_plus_b2 - &v1) * P::NONRESIDUE;
|
||||
|
||||
// c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + NONRESIDUE * v2
|
||||
// = (a0 + a1) * b1 - v1
|
||||
let c1 = a0_plus_a1 * c1 - &v1;
|
||||
// c2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1
|
||||
// = v1
|
||||
let c2 = v1;
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
|
||||
/// Multiplies `self` by a sparse element which has `c2 == zero`.
|
||||
pub fn mul_by_c0_c1_0(
|
||||
&self,
|
||||
c0: &Fp2Var<P::Fp2Params>,
|
||||
c1: &Fp2Var<P::Fp2Params>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let v0 = &self.c0 * c0;
|
||||
let v1 = &self.c1 * c1;
|
||||
// v2 = 0.
|
||||
|
||||
let a1_plus_a2 = &self.c1 + &self.c2;
|
||||
let a0_plus_a1 = &self.c0 + &self.c1;
|
||||
let a0_plus_a2 = &self.c0 + &self.c2;
|
||||
|
||||
let b1_plus_b2 = c1.clone();
|
||||
let b0_plus_b1 = c0 + c1;
|
||||
let b0_plus_b2 = c0.clone();
|
||||
|
||||
let c0 = (&a1_plus_a2 * &b1_plus_b2 - &v1) * P::NONRESIDUE + &v0;
|
||||
|
||||
let c1 = a0_plus_a1 * &b0_plus_b1 - &v0 - &v1;
|
||||
|
||||
let c2 = a0_plus_a2 * &b0_plus_b2 - &v0 + &v1;
|
||||
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp6Parameters> MulAssign<Fp2<P::Fp2Params>> for Fp6Var<P> {
|
||||
fn mul_assign(&mut self, other: Fp2<P::Fp2Params>) {
|
||||
self.c0 *= other;
|
||||
self.c1 *= other;
|
||||
self.c2 *= other;
|
||||
}
|
||||
}
|
||||
207
src/fields/mod.rs
Normal file
207
src/fields/mod.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
use ark_ff::{prelude::*, BitIteratorBE};
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
use core::{
|
||||
fmt::Debug,
|
||||
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
|
||||
};
|
||||
|
||||
use crate::{prelude::*, Assignment};
|
||||
|
||||
/// This module contains a generic implementation of cubic extension field
|
||||
/// variables. That is, it implements the R1CS equivalent of
|
||||
/// `ark_ff::CubicExtField`.
|
||||
pub mod cubic_extension;
|
||||
/// This module contains a generic implementation of quadratic extension field
|
||||
/// variables. That is, it implements the R1CS equivalent of
|
||||
/// `ark_ff::QuadExtField`.
|
||||
pub mod quadratic_extension;
|
||||
|
||||
/// This module contains a generic implementation of prime field variables.
|
||||
/// That is, it implements the R1CS equivalent of `ark_ff::Fp*`.
|
||||
pub mod fp;
|
||||
|
||||
/// This module contains a generic implementation of the degree-12 tower
|
||||
/// extension field. That is, it implements the R1CS equivalent of
|
||||
/// `ark_ff::Fp12`
|
||||
pub mod fp12;
|
||||
/// This module contains a generic implementation of the degree-2 tower
|
||||
/// extension field. That is, it implements the R1CS equivalent of
|
||||
/// `ark_ff::Fp2`
|
||||
pub mod fp2;
|
||||
/// This module contains a generic implementation of the degree-3 tower
|
||||
/// extension field. That is, it implements the R1CS equivalent of
|
||||
/// `ark_ff::Fp3`
|
||||
pub mod fp3;
|
||||
/// This module contains a generic implementation of the degree-4 tower
|
||||
/// extension field. That is, it implements the R1CS equivalent of
|
||||
/// `ark_ff::Fp4`
|
||||
pub mod fp4;
|
||||
/// This module contains a generic implementation of the degree-6 tower
|
||||
/// extension field. That is, it implements the R1CS equivalent of
|
||||
/// `ark_ff::fp6_2over3::Fp6`
|
||||
pub mod fp6_2over3;
|
||||
/// This module contains a generic implementation of the degree-6 tower
|
||||
/// extension field. That is, it implements the R1CS equivalent of
|
||||
/// `ark_ff::fp6_3over2::Fp6`
|
||||
pub mod fp6_3over2;
|
||||
|
||||
/// This trait is a hack used to work around the lack of implied bounds.
|
||||
pub trait FieldOpsBounds<'a, F, T: 'a>:
|
||||
Sized
|
||||
+ Add<&'a T, Output = T>
|
||||
+ Sub<&'a T, Output = T>
|
||||
+ Mul<&'a T, Output = T>
|
||||
+ Add<T, Output = T>
|
||||
+ Sub<T, Output = T>
|
||||
+ Mul<T, Output = T>
|
||||
+ Add<F, Output = T>
|
||||
+ Sub<F, Output = T>
|
||||
+ Mul<F, Output = T>
|
||||
{
|
||||
}
|
||||
|
||||
/// A variable representing a field. Corresponds to the native type `F`.
|
||||
pub trait FieldVar<F: Field, ConstraintF: Field>:
|
||||
'static
|
||||
+ Clone
|
||||
+ From<Boolean<ConstraintF>>
|
||||
+ R1CSVar<ConstraintF, Value = F>
|
||||
+ EqGadget<ConstraintF>
|
||||
+ ToBitsGadget<ConstraintF>
|
||||
+ AllocVar<F, ConstraintF>
|
||||
+ ToBytesGadget<ConstraintF>
|
||||
+ CondSelectGadget<ConstraintF>
|
||||
+ for<'a> FieldOpsBounds<'a, F, Self>
|
||||
+ 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
|
||||
{
|
||||
/// Returns the constant `F::zero()`.
|
||||
fn zero() -> Self;
|
||||
|
||||
/// Returns a `Boolean` representing whether `self == Self::zero()`.
|
||||
fn is_zero(&self) -> Result<Boolean<ConstraintF>, SynthesisError> {
|
||||
self.is_eq(&Self::zero())
|
||||
}
|
||||
|
||||
/// Returns the constant `F::one()`.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Returns a `Boolean` representing whether `self == Self::one()`.
|
||||
fn is_one(&self) -> Result<Boolean<ConstraintF>, SynthesisError> {
|
||||
self.is_eq(&Self::one())
|
||||
}
|
||||
|
||||
/// Returns a constant with value `v`.
|
||||
///
|
||||
/// This *should not* allocate any variables.
|
||||
fn constant(v: F) -> Self;
|
||||
|
||||
/// Computes `self + self`.
|
||||
fn double(&self) -> Result<Self, SynthesisError> {
|
||||
Ok(self.clone() + self)
|
||||
}
|
||||
|
||||
/// Sets `self = self + self`.
|
||||
fn double_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
||||
*self += self.double()?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Coputes `-self`.
|
||||
fn negate(&self) -> Result<Self, SynthesisError>;
|
||||
|
||||
/// Sets `self = -self`.
|
||||
#[inline]
|
||||
fn negate_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.negate()?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Computes `self * self`.
|
||||
///
|
||||
/// A default implementation is provided which just invokes the underlying
|
||||
/// multiplication routine. However, this method should be specialized
|
||||
/// for extension fields, where faster algorithms exist for squaring.
|
||||
fn square(&self) -> Result<Self, SynthesisError> {
|
||||
Ok(self.clone() * self)
|
||||
}
|
||||
|
||||
/// Sets `self = self.square()`.
|
||||
fn square_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.square()?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Enforces that `self * other == result`.
|
||||
fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> {
|
||||
let actual_result = self.clone() * other;
|
||||
result.enforce_equal(&actual_result)
|
||||
}
|
||||
|
||||
/// Enforces that `self * self == result`.
|
||||
fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> {
|
||||
let actual_result = self.square()?;
|
||||
result.enforce_equal(&actual_result)
|
||||
}
|
||||
|
||||
/// Computes `result` such that `self * result == Self::one()`.
|
||||
fn inverse(&self) -> Result<Self, SynthesisError>;
|
||||
|
||||
/// Returns `(self / denominator)`. but requires fewer constraints than
|
||||
/// `self * denominator.inverse()`.
|
||||
/// It is up to the caller to ensure that denominator is non-zero,
|
||||
/// since in that case the result is unconstrained.
|
||||
fn mul_by_inverse(&self, denominator: &Self) -> Result<Self, SynthesisError> {
|
||||
let result = Self::new_witness(self.cs(), || {
|
||||
let denominator_inv_native = denominator.value()?.inverse().get()?;
|
||||
let result = self.value()? * &denominator_inv_native;
|
||||
Ok(result)
|
||||
})?;
|
||||
result.mul_equals(&denominator, &self)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Computes the frobenius map over `self`.
|
||||
fn frobenius_map(&self, power: usize) -> Result<Self, SynthesisError>;
|
||||
|
||||
/// Sets `self = self.frobenius_map()`.
|
||||
fn frobenius_map_in_place(&mut self, power: usize) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.frobenius_map(power)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Comptues `self^bits`, where `bits` is a *little-endian* bit-wise
|
||||
/// decomposition of the exponent.
|
||||
fn pow_le(&self, bits: &[Boolean<ConstraintF>]) -> Result<Self, SynthesisError> {
|
||||
let mut res = Self::one();
|
||||
let mut power = self.clone();
|
||||
for bit in bits {
|
||||
let tmp = res.clone() * &power;
|
||||
res = bit.select(&tmp, &res)?;
|
||||
power.square_in_place()?;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Computes `self^S`, where S is interpreted as an little-endian
|
||||
/// u64-decomposition of an integer.
|
||||
fn pow_by_constant<S: AsRef<[u64]>>(&self, exp: S) -> Result<Self, SynthesisError> {
|
||||
let mut res = Self::one();
|
||||
for i in BitIteratorBE::without_leading_zeros(exp) {
|
||||
res.square_in_place()?;
|
||||
if i {
|
||||
res *= self;
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
561
src/fields/quadratic_extension.rs
Normal file
561
src/fields/quadratic_extension.rs
Normal file
@@ -0,0 +1,561 @@
|
||||
use ark_ff::{
|
||||
fields::{Field, QuadExtField, QuadExtParameters},
|
||||
Zero,
|
||||
};
|
||||
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use crate::{
|
||||
fields::{fp::FpVar, FieldOpsBounds, FieldVar},
|
||||
prelude::*,
|
||||
ToConstraintFieldGadget, Vec,
|
||||
};
|
||||
|
||||
/// This struct is the `R1CS` equivalent of the quadratic extension field type
|
||||
/// in `ark-ff`, i.e. `ark_ff::QuadExtField`.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))]
|
||||
#[must_use]
|
||||
pub struct QuadExtVar<BF: FieldVar<P::BaseField, P::BasePrimeField>, P: QuadExtVarParams<BF>>
|
||||
where
|
||||
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||
{
|
||||
/// The zero-th coefficient of this field element.
|
||||
pub c0: BF,
|
||||
/// The first coefficient of this field element.
|
||||
pub c1: BF,
|
||||
#[derivative(Debug = "ignore")]
|
||||
_params: PhantomData<P>,
|
||||
}
|
||||
|
||||
/// This trait describes parameters that are used to implement arithmetic for
|
||||
/// `QuadExtVar`.
|
||||
pub trait QuadExtVarParams<BF: FieldVar<Self::BaseField, Self::BasePrimeField>>:
|
||||
QuadExtParameters
|
||||
where
|
||||
for<'a> &'a BF: FieldOpsBounds<'a, Self::BaseField, BF>,
|
||||
{
|
||||
/// Multiply the base field of the `QuadExtVar` by the appropriate Frobenius
|
||||
/// coefficient. This is equivalent to
|
||||
/// `Self::mul_base_field_by_frob_coeff(power)`.
|
||||
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>,
|
||||
{
|
||||
/// Constructs a `QuadExtVar` from the underlying coefficients.
|
||||
pub fn new(c0: BF, c1: BF) -> Self {
|
||||
Self {
|
||||
c0,
|
||||
c1,
|
||||
_params: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplies a variable of the base field by the quadratic nonresidue
|
||||
/// `P::NONRESIDUE` that is used to construct the extension field.
|
||||
#[inline]
|
||||
pub fn mul_base_field_by_nonresidue(fe: &BF) -> Result<BF, SynthesisError> {
|
||||
Ok(fe * P::NONRESIDUE)
|
||||
}
|
||||
|
||||
/// Multiplies `self` by a constant from the base field.
|
||||
#[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)
|
||||
}
|
||||
|
||||
/// Sets `self = self.mul_by_base_field_constant(fe)`.
|
||||
#[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]
|
||||
#[tracing::instrument(target = "r1cs", skip(exponent))]
|
||||
pub fn cyclotomic_exp(&self, exponent: impl AsRef<[u64]>) -> Result<Self, SynthesisError>
|
||||
where
|
||||
Self: FieldVar<QuadExtField<P>, P::BasePrimeField>,
|
||||
{
|
||||
let mut res = Self::one();
|
||||
let self_inverse = self.unitary_inverse()?;
|
||||
|
||||
let mut found_nonzero = false;
|
||||
let naf = ark_ff::biginteger::arithmetic::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) -> 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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn double(&self) -> Result<Self, SynthesisError> {
|
||||
let c0 = self.c0.double()?;
|
||||
let c1 = self.c1.double()?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn negate(&self) -> Result<Self, SynthesisError> {
|
||||
let mut result = self.clone();
|
||||
result.c0.negate_in_place()?;
|
||||
result.c1.negate_in_place()?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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))
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn inverse(&self) -> Result<Self, SynthesisError> {
|
||||
let mode = if self.is_constant() {
|
||||
AllocationMode::Constant
|
||||
} else {
|
||||
AllocationMode::Witness
|
||||
};
|
||||
let inverse = Self::new_variable(
|
||||
self.cs(),
|
||||
|| {
|
||||
self.value()
|
||||
.map(|f| f.inverse().unwrap_or(QuadExtField::zero()))
|
||||
},
|
||||
mode,
|
||||
)?;
|
||||
self.mul_equals(&inverse, &Self::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>,
|
||||
{
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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]
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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>,
|
||||
{
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn to_bits_le(&self) -> Result<Vec<Boolean<P::BasePrimeField>>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bits_le()?;
|
||||
let mut c1 = self.c1.to_bits_le()?;
|
||||
c0.append(&mut c1);
|
||||
Ok(c0)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<P::BasePrimeField>>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_non_unique_bits_le()?;
|
||||
let mut c1 = self.c1.to_non_unique_bits_le()?;
|
||||
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>,
|
||||
{
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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)
|
||||
}
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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> ToConstraintFieldGadget<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>,
|
||||
BF: ToConstraintFieldGadget<P::BasePrimeField>,
|
||||
{
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
fn to_constraint_field(&self) -> Result<Vec<FpVar<P::BasePrimeField>>, SynthesisError> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
res.extend_from_slice(&self.c0.to_constraint_field()?);
|
||||
res.extend_from_slice(&self.c1.to_constraint_field()?);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
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>;
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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>;
|
||||
|
||||
#[tracing::instrument(target = "r1cs")]
|
||||
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(ark_relations::ns!(cs, "c0"), || c0, mode)?;
|
||||
let c1 = BF::new_variable(ark_relations::ns!(cs, "c1"), || c1, mode)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user