mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-12 00:41:32 +01:00
MNT4/6 curves and recursive SNARKs (#150)
* Add mnt6_753 curve Generalize mnt6 curve model * Add mnt4 curves * Use resampled generators * Calculate correct G2 cofactors * Add fields to r1cs-std * Add pairings * Improve reusing of Fq/Fr among MNT curves * Add instantiations of curves Fix Fp6_2over3 Rebase code to current master * Add test for recursive NIZK proof verification * Address comments in PR * Improve test case and port to GM17 Also fix a minor bug in to_field_vec
This commit is contained in:
@@ -270,6 +270,10 @@ impl<F: PrimeField> FieldGadget<F, F> for FpGadget<F> {
|
||||
1
|
||||
}
|
||||
|
||||
fn cost_of_mul_equals() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
@@ -642,6 +642,10 @@ where
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn cost_of_mul_equals() -> usize {
|
||||
Self::cost_of_mul()
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
|
||||
}
|
||||
|
||||
@@ -453,6 +453,10 @@ impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> FieldGadget<Fp
|
||||
3
|
||||
}
|
||||
|
||||
fn cost_of_mul_equals() -> usize {
|
||||
Self::cost_of_mul()
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
932
r1cs-std/src/fields/fp3.rs
Normal file
932
r1cs-std/src/fields/fp3.rs
Normal file
@@ -0,0 +1,932 @@
|
||||
use algebra::{
|
||||
fields::{
|
||||
fp3::{Fp3, Fp3Parameters},
|
||||
Field,
|
||||
},
|
||||
PrimeField, SquareRootField,
|
||||
};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError};
|
||||
|
||||
use crate::{fields::fp::FpGadget, prelude::*, Assignment, Vec};
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(
|
||||
bound = "P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField"
|
||||
))]
|
||||
#[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>
|
||||
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))
|
||||
}
|
||||
|
||||
/// Use the Toom-Cook-3x method to compute multiplication.
|
||||
#[inline]
|
||||
fn square<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
// Uses Toom-Cook-3x multiplication from
|
||||
//
|
||||
// Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
||||
// v0 = a(0)^2 = a0^2
|
||||
let v0 = self.c0.square(&mut cs.ns(|| "Calc v0"))?;
|
||||
|
||||
// v1 = a(1)^2 = (a0 + a1 + a2)^2
|
||||
let v1 = {
|
||||
let a0_plus_a1_plus_a2 = self
|
||||
.c0
|
||||
.add(cs.ns(|| "a0 + a1"), &self.c1)?
|
||||
.add(cs.ns(|| "a0 + a1 + a2"), &self.c2)?;
|
||||
a0_plus_a1_plus_a2.square(&mut cs.ns(|| "(a0 + a1 + a2)^2"))?
|
||||
};
|
||||
|
||||
// v2 = a(−1)^2 = (a0 − a1 + a2)^2
|
||||
let v2 = {
|
||||
let a0_minus_a1_plus_a2 = self
|
||||
.c0
|
||||
.sub(cs.ns(|| "a0 - a1"), &self.c1)?
|
||||
.add(cs.ns(|| "a0 - a2 + a2"), &self.c2)?;
|
||||
a0_minus_a1_plus_a2.square(&mut cs.ns(|| "(a0 - a1 + a2)^2"))?
|
||||
};
|
||||
|
||||
// v3 = a(2)^2 = (a0 + 2a1 + 4a2)^2
|
||||
let v3 = {
|
||||
let a1_double = self.c1.double(cs.ns(|| "2a1"))?;
|
||||
let a2_quad = self.c2.double(cs.ns(|| "2a2"))?.double(cs.ns(|| "4a2"))?;
|
||||
let a0_plus_2_a1_plus_4_a2 = self
|
||||
.c0
|
||||
.add(cs.ns(|| "a0 + 2a1"), &a1_double)?
|
||||
.add(cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?;
|
||||
|
||||
a0_plus_2_a1_plus_4_a2.square(&mut cs.ns(|| "(a0 + 2a1 + 4a2)^2"))?
|
||||
};
|
||||
|
||||
// v4 = a(∞)^2 = a2^2
|
||||
let v4 = self.c2.square(&mut cs.ns(|| "a2^2"))?;
|
||||
|
||||
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(|| "one_sixth_v2"), &six_inverse)?;
|
||||
let one_sixth_v3 = v3.mul_by_constant(cs.ns(|| "one_sixth_v3"), &six_inverse)?;
|
||||
let two_v4 = v4.double(cs.ns(|| "double_v4"))?;
|
||||
|
||||
// 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 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(c0_cs.ns(|| "mul_by_res"), &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(c1_cs.ns(|| "v2_by_3"))?;
|
||||
let non_residue_v4 = v4.mul_by_constant(c1_cs.ns(|| "mul_by_res"), &P::NONRESIDUE)?;
|
||||
|
||||
half_v0
|
||||
.negate(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(c2_cs.ns(|| "half_v2"), &two_inverse)?;
|
||||
half_v1
|
||||
.add(c2_cs.ns(|| "add1"), &half_v2)?
|
||||
.sub(c2_cs.ns(|| "sub1"), &v4)?
|
||||
.sub(c2_cs.ns(|| "sub2"), &v0)?
|
||||
};
|
||||
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
|
||||
// 18 constraints, we can probably do better but not sure it's worth it.
|
||||
#[inline]
|
||||
fn inverse<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
|
||||
self.get_value().and_then(|val| val.inverse()).get()
|
||||
})?;
|
||||
let one = Self::one(cs.ns(|| "one"))?;
|
||||
inverse.mul_equals(cs.ns(|| "check inverse"), &self, &one)?;
|
||||
Ok(inverse)
|
||||
}
|
||||
|
||||
#[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,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut result = self.clone();
|
||||
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.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_inv() -> usize {
|
||||
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
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<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))
|
||||
}
|
||||
}
|
||||
772
r1cs-std/src/fields/fp4.rs
Normal file
772
r1cs-std/src/fields/fp4.rs
Normal file
@@ -0,0 +1,772 @@
|
||||
use algebra::{
|
||||
fields::{Fp2, Fp2Parameters, Fp4, Fp4Parameters},
|
||||
BigInteger, Field, One, PrimeField,
|
||||
};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
||||
use crate::{prelude::*, Assignment, Vec};
|
||||
|
||||
type Fp2Gadget<P, ConstraintF> =
|
||||
super::fp2::Fp2Gadget<<P as Fp4Parameters>::Fp2Params, ConstraintF>;
|
||||
type Fp2GadgetVariable<P, ConstraintF> = <Fp2Gadget<P, ConstraintF> as FieldGadget<
|
||||
Fp2<<P as Fp4Parameters>::Fp2Params>,
|
||||
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> 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))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inverse<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
|
||||
self.get_value().and_then(|val| val.inverse()).get()
|
||||
})?;
|
||||
|
||||
// Karatsuba multiplication for Fp4 with the inverse:
|
||||
// v0 = A.c0 * B.c0
|
||||
// v1 = A.c1 * B.c1
|
||||
//
|
||||
// 1 = v0 + non_residue * v1
|
||||
// => v0 = 1 - non_residue * v1
|
||||
//
|
||||
// 0 = result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
// => v0 + v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
// Enforced with 2 constraints:
|
||||
// A.c1 * B.c1 = v1
|
||||
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
// Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
||||
// Constraint 1
|
||||
let v1 = self.c1.mul(cs.ns(|| "inv_constraint_1"), &inverse.c1)?;
|
||||
|
||||
// Constraint 2
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
let b0_plus_b1 = inverse.c0.add(cs.ns(|| "b0 + b1"), &inverse.c1)?;
|
||||
|
||||
let one = Fp2::<<P as Fp4Parameters>::Fp2Params>::one();
|
||||
let rhs = Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "nr * v1"), &v1)?
|
||||
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
.negate(cs.ns(|| "negate it"))?
|
||||
.add_constant(cs.ns(|| "add one"), &one)?;
|
||||
a0_plus_a1.mul_equals(cs.ns(|| "inv_constraint_2"), &b0_plus_b1, &rhs)?;
|
||||
Ok(inverse)
|
||||
}
|
||||
|
||||
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> as FieldGadget<Fp2<P::Fp2Params>, ConstraintF>>::cost_of_mul(
|
||||
)
|
||||
}
|
||||
|
||||
fn cost_of_mul_equals() -> usize {
|
||||
Self::cost_of_mul()
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
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<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))
|
||||
}
|
||||
}
|
||||
763
r1cs-std/src/fields/fp6_2over3.rs
Normal file
763
r1cs-std/src/fields/fp6_2over3.rs
Normal file
@@ -0,0 +1,763 @@
|
||||
use algebra::{
|
||||
fields::{
|
||||
fp6_2over3::{Fp6, Fp6Parameters},
|
||||
Fp3, Fp3Parameters,
|
||||
},
|
||||
BigInteger, Field, One, PrimeField, SquareRootField,
|
||||
};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
||||
use crate::{prelude::*, Assignment, Vec};
|
||||
|
||||
type Fp3Gadget<P, ConstraintF> =
|
||||
super::fp3::Fp3Gadget<<P as Fp6Parameters>::Fp3Params, ConstraintF>;
|
||||
type Fp3GadgetVariable<P, ConstraintF> = <Fp3Gadget<P, ConstraintF> as FieldGadget<
|
||||
Fp3<<P as Fp6Parameters>::Fp3Params>,
|
||||
ConstraintF,
|
||||
>>::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> 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))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inverse<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
|
||||
self.get_value().and_then(|val| val.inverse()).get()
|
||||
})?;
|
||||
|
||||
// Karatsuba multiplication for Fp6 with the inverse:
|
||||
// v0 = A.c0 * B.c0
|
||||
// v1 = A.c1 * B.c1
|
||||
//
|
||||
// 1 = v0 + non_residue * v1
|
||||
// => v0 = 1 - non_residue * v1
|
||||
//
|
||||
// 0 = result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
// => v0 + v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
// Enforced with 2 constraints:
|
||||
// A.c1 * B.c1 = v1
|
||||
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
// Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
||||
// Constraint 1
|
||||
let v1 = self.c1.mul(cs.ns(|| "inv_constraint_1"), &inverse.c1)?;
|
||||
|
||||
// Constraint 2
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
let b0_plus_b1 = inverse.c0.add(cs.ns(|| "b0 + b1"), &inverse.c1)?;
|
||||
|
||||
let one = Fp3::<<P as Fp6Parameters>::Fp3Params>::one();
|
||||
let rhs = Self::mul_fp3_gadget_by_nonresidue(cs.ns(|| "nr * v1"), &v1)?
|
||||
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
.negate(cs.ns(|| "negate it"))?
|
||||
.add_constant(cs.ns(|| "add one"), &one)?;
|
||||
a0_plus_a1.mul_equals(cs.ns(|| "inv_constraint_2"), &b0_plus_b1, &rhs)?;
|
||||
Ok(inverse)
|
||||
}
|
||||
|
||||
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 {
|
||||
3 * <Fp3Gadget<P, ConstraintF> as FieldGadget<Fp3<P::Fp3Params>, ConstraintF>>::cost_of_mul(
|
||||
)
|
||||
}
|
||||
|
||||
fn cost_of_mul_equals() -> usize {
|
||||
Self::cost_of_mul()
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
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<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))
|
||||
}
|
||||
}
|
||||
@@ -259,7 +259,7 @@ where
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
// Uses Toom-Cool-3x multiplication from
|
||||
// Uses Toom-Cook-3x multiplication from
|
||||
//
|
||||
// Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
@@ -404,7 +404,7 @@ where
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
// Uses Toom-Cool-3x multiplication from
|
||||
// Uses Toom-Cook-3x multiplication from
|
||||
//
|
||||
// Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
@@ -500,7 +500,7 @@ where
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
|
||||
// 18 constaints, we can probably do better but not sure it's worth it.
|
||||
// 18 constraints, we can probably do better but not sure it's worth it.
|
||||
#[inline]
|
||||
fn inverse<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
|
||||
@@ -7,6 +7,9 @@ use crate::prelude::*;
|
||||
pub mod fp;
|
||||
pub mod fp12;
|
||||
pub mod fp2;
|
||||
pub mod fp3;
|
||||
pub mod fp4;
|
||||
pub mod fp6_2over3;
|
||||
pub mod fp6_3over2;
|
||||
|
||||
pub trait FieldGadget<F: Field, ConstraintF: Field>:
|
||||
@@ -248,6 +251,10 @@ pub trait FieldGadget<F: Field, ConstraintF: Field>:
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user