mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-12 00:41:32 +01:00
Move snark-gadgets to r1cs-std
This commit is contained in:
10
r1cs-std/src/fields/bls12_377.rs
Normal file
10
r1cs-std/src/fields/bls12_377.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use algebra::{
|
||||
fields::bls12_377::{Fq, Fq12Parameters, Fq2Parameters, Fq6Parameters},
|
||||
};
|
||||
|
||||
use super::{fp::FpGadget, fp12::Fp12Gadget, fp2::Fp2Gadget, fp6_3over2::Fp6Gadget};
|
||||
|
||||
pub type FqGadget = FpGadget<Fq>;
|
||||
pub type Fq2Gadget = Fp2Gadget<Fq2Parameters, Fq>;
|
||||
pub type Fq6Gadget = Fp6Gadget<Fq6Parameters, Fq>;
|
||||
pub type Fq12Gadget = Fp12Gadget<Fq12Parameters, Fq>;
|
||||
4
r1cs-std/src/fields/edwards_bls12.rs
Normal file
4
r1cs-std/src/fields/edwards_bls12.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
use algebra::fields::edwards_bls12::fq::Fq;
|
||||
use crate::fields::fp::FpGadget;
|
||||
|
||||
pub type FqGadget = FpGadget<Fq>;
|
||||
4
r1cs-std/src/fields/edwards_sw6.rs
Normal file
4
r1cs-std/src/fields/edwards_sw6.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
use algebra::fields::edwards_sw6::fq::Fq;
|
||||
use crate::fields::fp::FpGadget;
|
||||
|
||||
pub type FqGadget = FpGadget<Fq>;
|
||||
571
r1cs-std/src/fields/fp.rs
Normal file
571
r1cs-std/src/fields/fp.rs
Normal file
@@ -0,0 +1,571 @@
|
||||
use algebra::{bytes::ToBytes, FpParameters, PrimeField};
|
||||
use r1cs_core::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use super::FieldGadget;
|
||||
use crate::{
|
||||
boolean::{AllocatedBit, Boolean},
|
||||
uint8::UInt8,
|
||||
};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::{
|
||||
Assignment,
|
||||
ConstraintVar::{self, *},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FpGadget<F: PrimeField> {
|
||||
pub value: Option<F>,
|
||||
pub variable: ConstraintVar<F>,
|
||||
}
|
||||
|
||||
impl<F: PrimeField> FpGadget<F> {
|
||||
#[inline]
|
||||
pub fn from<CS: ConstraintSystem<F>>(mut cs: CS, value: &F) -> Self {
|
||||
Self::alloc(cs.ns(|| "from"), || Ok(*value)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> FieldGadget<F, F> for FpGadget<F> {
|
||||
type Variable = ConstraintVar<F>;
|
||||
|
||||
#[inline]
|
||||
fn get_value(&self) -> Option<F> {
|
||||
self.value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_variable(&self) -> Self::Variable {
|
||||
self.variable.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn zero<CS: ConstraintSystem<F>>(_cs: CS) -> Result<Self, SynthesisError> {
|
||||
let value = Some(F::zero());
|
||||
Ok(FpGadget {
|
||||
value,
|
||||
variable: ConstraintVar::zero(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn one<CS: ConstraintSystem<F>>(_cs: CS) -> Result<Self, SynthesisError> {
|
||||
let value = Some(F::one());
|
||||
Ok(FpGadget {
|
||||
value,
|
||||
variable: CS::one().into(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut _cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let value = match (self.value, other.value) {
|
||||
(Some(val1), Some(val2)) => Some(val1 + &val2),
|
||||
(..) => None,
|
||||
};
|
||||
|
||||
Ok(FpGadget {
|
||||
value,
|
||||
variable: &self.variable + &other.variable,
|
||||
})
|
||||
}
|
||||
|
||||
fn double<CS: ConstraintSystem<F>>(&self, _cs: CS) -> Result<Self, SynthesisError> {
|
||||
let value = self.value.map(|val| val.double());
|
||||
let mut variable = self.variable.clone();
|
||||
variable.double_in_place();
|
||||
Ok(FpGadget { value, variable })
|
||||
}
|
||||
|
||||
fn double_in_place<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
_cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.value.as_mut().map(|val| val.double_in_place());
|
||||
self.variable.double_in_place();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut _cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let value = match (self.value, other.value) {
|
||||
(Some(val1), Some(val2)) => Some(val1 - &val2),
|
||||
(..) => None,
|
||||
};
|
||||
|
||||
Ok(FpGadget {
|
||||
value,
|
||||
variable: &self.variable - &other.variable,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn negate<CS: ConstraintSystem<F>>(&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<F>>(
|
||||
&mut self,
|
||||
_cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.value.as_mut().map(|val| *val = -(*val));
|
||||
self.variable.negate_in_place();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let product = Self::alloc(cs.ns(|| "mul"), || {
|
||||
Ok(self.value.get()? * &other.value.get()?)
|
||||
})?;
|
||||
cs.enforce(
|
||||
|| "mul_constraint",
|
||||
|lc| &self.variable + lc,
|
||||
|lc| &other.variable + lc,
|
||||
|lc| &product.variable + lc,
|
||||
);
|
||||
Ok(product)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_constant<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
_cs: CS,
|
||||
other: &F,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let value = self.value.map(|val| val + other);
|
||||
Ok(FpGadget {
|
||||
value,
|
||||
variable: self.variable.clone() + (*other, CS::one()),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_constant_in_place<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
_cs: CS,
|
||||
other: &F,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.value.as_mut().map(|val| *val += other);
|
||||
self.variable += (*other, CS::one());
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul_by_constant<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
cs: CS,
|
||||
other: &F,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut result = self.clone();
|
||||
result.mul_by_constant_in_place(cs, other)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul_by_constant_in_place<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
mut _cs: CS,
|
||||
other: &F,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.value.as_mut().map(|val| *val *= other);
|
||||
self.variable *= *other;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inverse<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
let inverse = Self::alloc(cs.ns(|| "inverse"), || {
|
||||
let result = self.value.get()?;
|
||||
let inv = result.inverse().expect("Inverse doesn't exist!");
|
||||
Ok(inv)
|
||||
})?;
|
||||
|
||||
let one = CS::one();
|
||||
cs.enforce(
|
||||
|| "inv_constraint",
|
||||
|lc| &self.variable + lc,
|
||||
|lc| &inverse.variable + lc,
|
||||
|lc| lc + one,
|
||||
);
|
||||
Ok(inverse)
|
||||
}
|
||||
|
||||
fn frobenius_map<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
_: CS,
|
||||
_: usize,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
fn frobenius_map_in_place<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
_: CS,
|
||||
_: usize,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn mul_equals<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
result: &Self,
|
||||
) -> Result<(), SynthesisError> {
|
||||
cs.enforce(
|
||||
|| "mul_constraint",
|
||||
|lc| &self.variable + lc,
|
||||
|lc| &other.variable + lc,
|
||||
|lc| &result.variable + lc,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn square_equals<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
result: &Self,
|
||||
) -> Result<(), SynthesisError> {
|
||||
cs.enforce(
|
||||
|| "sqr_constraint",
|
||||
|lc| &self.variable + lc,
|
||||
|lc| &self.variable + lc,
|
||||
|lc| &result.variable + lc,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cost_of_mul() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> PartialEq for FpGadget<F> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
!self.value.is_none() && !other.value.is_none() && self.value == other.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> Eq for FpGadget<F> {}
|
||||
|
||||
impl<F: PrimeField> EqGadget<F> for FpGadget<F> {}
|
||||
|
||||
impl<F: PrimeField> ConditionalEqGadget<F> for FpGadget<F> {
|
||||
#[inline]
|
||||
fn conditional_enforce_equal<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
condition: &Boolean,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let difference = self.sub(cs.ns(|| "difference"), other)?;
|
||||
let one = CS::one();
|
||||
let one_const = F::one();
|
||||
cs.enforce(
|
||||
|| "conditional_equals",
|
||||
|lc| &difference.variable + lc,
|
||||
|lc| lc + &condition.lc(one, one_const),
|
||||
|lc| lc,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> NEqGadget<F> for FpGadget<F> {
|
||||
#[inline]
|
||||
fn enforce_not_equal<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let a_minus_b = self.sub(cs.ns(|| "A - B"), other)?;
|
||||
a_minus_b.inverse(cs.ns(|| "Enforce inverse exists"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> ToBitsGadget<F> for FpGadget<F> {
|
||||
/// Outputs the binary representation of the value in `self` in *big-endian*
|
||||
/// form.
|
||||
fn to_bits<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
let num_bits = F::Params::MODULUS_BITS;
|
||||
use algebra::BitIterator;
|
||||
let bit_values = match self.value {
|
||||
Some(value) => {
|
||||
let mut field_char = BitIterator::new(F::characteristic());
|
||||
let mut tmp = Vec::with_capacity(num_bits as usize);
|
||||
let mut found_one = false;
|
||||
for b in BitIterator::new(value.into_repr()) {
|
||||
// Skip leading bits
|
||||
found_one |= field_char.next().unwrap();
|
||||
if !found_one {
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp.push(Some(b));
|
||||
}
|
||||
|
||||
assert_eq!(tmp.len(), num_bits as usize);
|
||||
|
||||
tmp
|
||||
},
|
||||
None => vec![None; num_bits as usize],
|
||||
};
|
||||
|
||||
let mut bits = vec![];
|
||||
for (i, b) in bit_values.into_iter().enumerate() {
|
||||
bits.push(AllocatedBit::alloc(cs.ns(|| format!("bit {}", i)), || {
|
||||
b.get()
|
||||
})?);
|
||||
}
|
||||
|
||||
let mut lc = LinearCombination::zero();
|
||||
let mut coeff = F::one();
|
||||
|
||||
for bit in bits.iter().rev() {
|
||||
lc = lc + (coeff, bit.get_variable());
|
||||
|
||||
coeff.double_in_place();
|
||||
}
|
||||
|
||||
lc = &self.variable - lc;
|
||||
|
||||
cs.enforce(|| "unpacking_constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
||||
Ok(bits.into_iter().map(Boolean::from).collect())
|
||||
}
|
||||
|
||||
fn to_bits_strict<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
let bits = self.to_bits(&mut cs)?;
|
||||
Boolean::enforce_in_field::<_, _, F>(&mut cs, &bits)?;
|
||||
|
||||
Ok(bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> ToBytesGadget<F> for FpGadget<F> {
|
||||
fn to_bytes<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
let byte_values = match self.value {
|
||||
Some(value) => to_bytes![&value.into_repr()]?
|
||||
.into_iter()
|
||||
.map(Some)
|
||||
.collect::<Vec<_>>(),
|
||||
None => {
|
||||
let default = F::default();
|
||||
let default_len = to_bytes![&default].unwrap().len();
|
||||
vec![None; default_len]
|
||||
},
|
||||
};
|
||||
|
||||
let bytes = UInt8::alloc_vec(cs.ns(|| "Alloc bytes"), &byte_values)?;
|
||||
|
||||
let mut lc = LinearCombination::zero();
|
||||
let mut coeff = F::one();
|
||||
|
||||
for bit in bytes
|
||||
.iter()
|
||||
.flat_map(|byte_gadget| byte_gadget.bits.clone())
|
||||
{
|
||||
match bit {
|
||||
Boolean::Is(bit) => {
|
||||
lc = lc + (coeff, bit.get_variable());
|
||||
coeff.double_in_place();
|
||||
},
|
||||
Boolean::Constant(_) | Boolean::Not(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
lc = &self.variable - lc;
|
||||
|
||||
cs.enforce(|| "unpacking_constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn to_bytes_strict<CS: ConstraintSystem<F>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
let bytes = self.to_bytes(&mut cs)?;
|
||||
Boolean::enforce_in_field::<_, _, F>(
|
||||
&mut cs,
|
||||
&bytes.iter()
|
||||
.flat_map(|byte_gadget| byte_gadget.into_bits_le())
|
||||
// This reverse maps the bits into big-endian form, as required by `enforce_in_field`.
|
||||
.rev()
|
||||
.collect::<Vec<_>>(),
|
||||
)?;
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> CondSelectGadget<F> for FpGadget<F> {
|
||||
#[inline]
|
||||
fn conditionally_select<CS: ConstraintSystem<F>>(
|
||||
mut cs: CS,
|
||||
cond: &Boolean,
|
||||
first: &Self,
|
||||
second: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
if let Boolean::Constant(cond) = *cond {
|
||||
if cond {
|
||||
Ok(first.clone())
|
||||
} else {
|
||||
Ok(second.clone())
|
||||
}
|
||||
} else {
|
||||
let result = Self::alloc(cs.ns(|| ""), || {
|
||||
cond.get_value()
|
||||
.and_then(|cond| if cond { first } else { second }.get_value())
|
||||
.get()
|
||||
})?;
|
||||
// a = self; b = other; c = cond;
|
||||
//
|
||||
// r = c * a + (1 - c) * b
|
||||
// r = b + c * (a - b)
|
||||
// c * (a - b) = r - b
|
||||
let one = CS::one();
|
||||
cs.enforce(
|
||||
|| "conditionally_select",
|
||||
|_| cond.lc(one, F::one()),
|
||||
|lc| (&first.variable - &second.variable) + lc,
|
||||
|lc| (&result.variable - &second.variable) + lc,
|
||||
);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
/// Uses two bits to perform a lookup into a table
|
||||
/// `b` is little-endian: `b[0]` is LSB.
|
||||
impl<F: PrimeField> TwoBitLookupGadget<F> for FpGadget<F> {
|
||||
type TableConstant = F;
|
||||
fn two_bit_lookup<CS: ConstraintSystem<F>>(
|
||||
mut cs: CS,
|
||||
b: &[Boolean],
|
||||
c: &[Self::TableConstant],
|
||||
) -> Result<Self, SynthesisError> {
|
||||
debug_assert!(b.len() == 2);
|
||||
debug_assert!(c.len() == 4);
|
||||
|
||||
let result = Self::alloc(cs.ns(|| "Allocate lookup result"), || {
|
||||
match (b[0].get_value().get()?, b[1].get_value().get()?) {
|
||||
(false, false) => Ok(c[0]),
|
||||
(false, true) => Ok(c[2]),
|
||||
(true, false) => Ok(c[1]),
|
||||
(true, true) => Ok(c[3]),
|
||||
}
|
||||
})?;
|
||||
let one = CS::one();
|
||||
cs.enforce(
|
||||
|| "Enforce lookup",
|
||||
|lc| lc + b[1].lc(one, c[3] - &c[2] - &c[1] + &c[0]) + (c[1] - &c[0], one),
|
||||
|lc| lc + b[0].lc(one, F::one()),
|
||||
|lc| result.get_variable() + lc + (-c[0], one) + b[1].lc(one, c[0] - &c[2]),
|
||||
);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> Clone for FpGadget<F> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
value: self.value.clone(),
|
||||
variable: self.variable.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> AllocGadget<F, F> for FpGadget<F> {
|
||||
#[inline]
|
||||
fn alloc<FN, T, CS: ConstraintSystem<F>>(
|
||||
mut cs: CS,
|
||||
value_gen: FN,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
T: Borrow<F>,
|
||||
{
|
||||
let mut value = None;
|
||||
let variable = cs.alloc(
|
||||
|| "alloc",
|
||||
|| {
|
||||
let tmp = *value_gen()?.borrow();
|
||||
value = Some(tmp);
|
||||
Ok(tmp)
|
||||
},
|
||||
)?;
|
||||
Ok(FpGadget {
|
||||
value,
|
||||
variable: Var(variable),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn alloc_input<FN, T, CS: ConstraintSystem<F>>(
|
||||
mut cs: CS,
|
||||
value_gen: FN,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
T: Borrow<F>,
|
||||
{
|
||||
let mut value = None;
|
||||
let variable = cs.alloc_input(
|
||||
|| "alloc",
|
||||
|| {
|
||||
let tmp = *value_gen()?.borrow();
|
||||
value = Some(tmp);
|
||||
Ok(tmp)
|
||||
},
|
||||
)?;
|
||||
Ok(FpGadget {
|
||||
value,
|
||||
variable: Var(variable),
|
||||
})
|
||||
}
|
||||
}
|
||||
854
r1cs-std/src/fields/fp12.rs
Normal file
854
r1cs-std/src/fields/fp12.rs
Normal file
@@ -0,0 +1,854 @@
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
||||
use algebra::{
|
||||
PrimeField, Field,
|
||||
fields::{
|
||||
fp12_2over3over2::{Fp12, Fp12Parameters},
|
||||
fp6_3over2::{Fp6, Fp6Parameters},
|
||||
Fp2Parameters,
|
||||
},
|
||||
BitIterator,
|
||||
};
|
||||
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::Assignment;
|
||||
|
||||
type Fp2Gadget<P, ConstraintF> =
|
||||
super::fp2::Fp2Gadget<<<P as Fp12Parameters>::Fp6Params as Fp6Parameters>::Fp2Params, ConstraintF>;
|
||||
type Fp6Gadget<P, ConstraintF> = super::fp6_3over2::Fp6Gadget<<P as Fp12Parameters>::Fp6Params, ConstraintF>;
|
||||
type Fp6GadgetVariable<P, ConstraintF> =
|
||||
<Fp6Gadget<P, ConstraintF> as FieldGadget<Fp6<<P as Fp12Parameters>::Fp6Params>, ConstraintF>>::Variable;
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = "ConstraintF: PrimeField"))]
|
||||
#[must_use]
|
||||
pub struct Fp12Gadget<P, ConstraintF: PrimeField>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
pub c0: Fp6Gadget<P, ConstraintF>,
|
||||
pub c1: Fp6Gadget<P, ConstraintF>,
|
||||
#[derivative(Debug = "ignore")]
|
||||
_params: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn new(c0: Fp6Gadget<P, ConstraintF>, c1: Fp6Gadget<P, ConstraintF>) -> Self {
|
||||
Self {
|
||||
c0,
|
||||
c1,
|
||||
_params: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply by quadratic nonresidue v.
|
||||
#[inline]
|
||||
pub(crate) fn mul_fp6_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
||||
cs: CS,
|
||||
fe: &Fp6Gadget<P, ConstraintF>,
|
||||
) -> Result<Fp6Gadget<P, ConstraintF>, SynthesisError> {
|
||||
let new_c0 = Fp6Gadget::<P, ConstraintF>::mul_fp2_gadget_by_nonresidue(cs, &fe.c2)?;
|
||||
let new_c1 = fe.c0.clone();
|
||||
let new_c2 = fe.c1.clone();
|
||||
Ok(Fp6Gadget::<P, ConstraintF>::new(new_c0, new_c1, new_c2))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn conjugate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c1.negate_in_place(cs)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Multiplies by an element of the form (c0 = (c0, c1, 0), c1 = (0, d1, 0))
|
||||
#[inline]
|
||||
pub fn mul_by_014<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
c0: &Fp2Gadget<P, ConstraintF>,
|
||||
c1: &Fp2Gadget<P, ConstraintF>,
|
||||
d1: &Fp2Gadget<P, ConstraintF>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let v0 = self.c0.mul_by_c0_c1_0(cs.ns(|| "v0"), &c0, &c1)?;
|
||||
let v1 = self.c1.mul_by_0_c1_0(cs.ns(|| "v1"), &d1)?;
|
||||
let new_c0 = Self::mul_fp6_by_nonresidue(cs.ns(|| "first mul_by_nr"), &v1)?
|
||||
.add(cs.ns(|| "v0 + nonresidue * v1"), &v0)?;
|
||||
|
||||
let c1 = {
|
||||
let tmp = c1.add(cs.ns(|| "c1 + d1"), &d1)?;
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
a0_plus_a1
|
||||
.mul_by_c0_c1_0(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &c0, &tmp)?
|
||||
.sub(cs.ns(|| "sub v0"), &v0)?
|
||||
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
};
|
||||
Ok(Self::new(new_c0, c1))
|
||||
}
|
||||
|
||||
/// Multiplies by an element of the form (c0 = (c0, 0, 0), c1 = (d0, d1, 0))
|
||||
#[inline]
|
||||
pub fn mul_by_034<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
c0: &Fp2Gadget<P, ConstraintF>,
|
||||
d0: &Fp2Gadget<P, ConstraintF>,
|
||||
d1: &Fp2Gadget<P, ConstraintF>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let a0 = self.c0.c0.mul(cs.ns(|| "a0"), &c0)?;
|
||||
let a1 = self.c0.c1.mul(cs.ns(|| "a1"), &c0)?;
|
||||
let a2 = self.c0.c2.mul(cs.ns(|| "a2"), &c0)?;
|
||||
let a = Fp6Gadget::<P, ConstraintF>::new(a0, a1, a2);
|
||||
let b = self.c1.mul_by_c0_c1_0(cs.ns(|| "b"), &d0, &d1)?;
|
||||
|
||||
let c0 = c0.add(cs.ns(|| "c0 + d0"), &d0)?;
|
||||
let c1 = d1;
|
||||
let e = self
|
||||
.c0
|
||||
.add(cs.ns(|| "self.c0 + self.c1"), &self.c1)?
|
||||
.mul_by_c0_c1_0(cs.ns(|| "compute e"), &c0, &c1)?;
|
||||
let a_plus_b = a.add(cs.ns(|| "a + b"), &b)?;
|
||||
let c1 = e.sub(cs.ns(|| "e - (a + b)"), &a_plus_b)?;
|
||||
let c0 = Self::mul_fp6_by_nonresidue(cs.ns(|| "b *nr"), &b)?.add(cs.ns(|| "plus a"), &a)?;
|
||||
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
pub fn cyclotomic_square<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut result = Self::zero(cs.ns(|| "alloc result"))?;
|
||||
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.mul(cs.ns(|| "first mul"), &z1)?;
|
||||
let t0 = {
|
||||
// (z0 + &z1) * &(z0 + &(fp2_nr * &z1)) - &tmp - &(tmp * &fp2_nr);
|
||||
let mut cs = cs.ns(|| "t0");
|
||||
let tmp1 = z0.add(cs.ns(|| "tmp1"), &z1)?;
|
||||
let tmp2 = z1
|
||||
.mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
|
||||
.add(cs.ns(|| "tmp2.2"), &z0)?;
|
||||
let tmp4 = tmp
|
||||
.mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
|
||||
.add(cs.ns(|| "tmp4.2"), &tmp)?;
|
||||
tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
|
||||
.sub(cs.ns(|| "tmp3.2"), &tmp4)?
|
||||
};
|
||||
let t1 = tmp.double(cs.ns(|| "t1"))?;
|
||||
|
||||
// t2 + t3*y = (z2 + z3*y)^2 = b^2
|
||||
let tmp = z2.mul(cs.ns(|| "second mul"), &z3)?;
|
||||
let t2 = {
|
||||
// (z2 + &z3) * &(z2 + &(fp2_nr * &z3)) - &tmp - &(tmp * &fp2_nr);
|
||||
let mut cs = cs.ns(|| "t2");
|
||||
let tmp1 = z2.add(cs.ns(|| "tmp1"), &z3)?;
|
||||
let tmp2 = z3
|
||||
.mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
|
||||
.add(cs.ns(|| "tmp2.2"), &z2)?;
|
||||
let tmp4 = tmp
|
||||
.mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
|
||||
.add(cs.ns(|| "tmp4.2"), &tmp)?;
|
||||
tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
|
||||
.sub(cs.ns(|| "tmp3.2"), &tmp4)?
|
||||
};
|
||||
let t3 = tmp.double(cs.ns(|| "t3"))?;
|
||||
|
||||
// t4 + t5*y = (z4 + z5*y)^2 = c^2
|
||||
let tmp = z4.mul(cs.ns(|| "third mul"), &z5)?;
|
||||
let t4 = {
|
||||
// (z4 + &z5) * &(z4 + &(fp2_nr * &z5)) - &tmp - &(tmp * &fp2_nr);
|
||||
let mut cs = cs.ns(|| "t4");
|
||||
let tmp1 = z4.add(cs.ns(|| "tmp1"), &z5)?;
|
||||
let tmp2 = z5
|
||||
.mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
|
||||
.add(cs.ns(|| "tmp2.2"), &z4)?;
|
||||
let tmp4 = tmp
|
||||
.mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
|
||||
.add(cs.ns(|| "tmp4.2"), &tmp)?;
|
||||
tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
|
||||
.sub(cs.ns(|| "tmp3.2"), &tmp4)?
|
||||
};
|
||||
let t5 = tmp.double(cs.ns(|| "t5"))?;
|
||||
|
||||
// for A
|
||||
|
||||
// z0 = 3 * t0 - 2 * z0
|
||||
result.c0.c0 = {
|
||||
let mut cs = cs.ns(|| "result.c0.c0");
|
||||
t0.sub(cs.ns(|| "1"), &z0)?
|
||||
.double(cs.ns(|| "2"))?
|
||||
.add(cs.ns(|| "3"), &t0)?
|
||||
};
|
||||
|
||||
// z1 = 3 * t1 + 2 * z1
|
||||
result.c1.c1 = {
|
||||
let mut cs = cs.ns(|| "result.c1.c1");
|
||||
t1.add(cs.ns(|| "1"), &z1)?
|
||||
.double(cs.ns(|| "2"))?
|
||||
.add(cs.ns(|| "3"), &t1)?
|
||||
};
|
||||
|
||||
// for B
|
||||
|
||||
// z2 = 3 * (xi * t5) + 2 * z2
|
||||
result.c1.c0 = {
|
||||
let mut cs = cs.ns(|| "result.c1.c0");
|
||||
let tmp = t5.mul_by_constant(cs.ns(|| "1"), &fp2_nr)?;
|
||||
z2.add(cs.ns(|| "2"), &tmp)?
|
||||
.double(cs.ns(|| "3"))?
|
||||
.add(cs.ns(|| "4"), &tmp)?
|
||||
};
|
||||
|
||||
// z3 = 3 * t4 - 2 * z3
|
||||
result.c0.c2 = {
|
||||
let mut cs = cs.ns(|| "result.c0.c2");
|
||||
t4.sub(cs.ns(|| "1"), &z3)?
|
||||
.double(cs.ns(|| "2"))?
|
||||
.add(cs.ns(|| "3"), &t4)?
|
||||
};
|
||||
|
||||
// for C
|
||||
|
||||
// z4 = 3 * t2 - 2 * z4
|
||||
result.c0.c1 = {
|
||||
let mut cs = cs.ns(|| "result.c0.c1");
|
||||
t2.sub(cs.ns(|| "1"), &z4)?
|
||||
.double(cs.ns(|| "2"))?
|
||||
.add(cs.ns(|| "3"), &t2)?
|
||||
};
|
||||
|
||||
// z5 = 3 * t3 + 2 * z5
|
||||
result.c1.c2 = {
|
||||
let mut cs = cs.ns(|| "result.c1.c2");
|
||||
t3.add(cs.ns(|| "1"), &z5)?
|
||||
.double(cs.ns(|| "2"))?
|
||||
.add(cs.ns(|| "3"), &t3)?
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cyclotomic_exp<CS: ConstraintSystem<ConstraintF>, S: AsRef<[u64]>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
exp: S,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut res = Self::one(cs.ns(|| "one"))?;
|
||||
let mut found_one = false;
|
||||
for (j, i) in BitIterator::new(exp).enumerate() {
|
||||
if found_one {
|
||||
res = res.cyclotomic_square(cs.ns(|| format!("res_square_{:?}", j)))?;
|
||||
} else {
|
||||
found_one = i;
|
||||
}
|
||||
if i {
|
||||
res.mul_in_place(cs.ns(|| format!("res_mul2_{:?}", j)), self)?;
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> FieldGadget<Fp12<P>, ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
type Variable = (Fp6GadgetVariable<P, ConstraintF>, Fp6GadgetVariable<P, ConstraintF>);
|
||||
|
||||
#[inline]
|
||||
fn get_value(&self) -> Option<Fp12<P>> {
|
||||
Some(Fp12::new(self.c0.get_value()?, self.c1.get_value()?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_variable(&self) -> Self::Variable {
|
||||
(self.c0.get_variable(), self.c1.get_variable())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
let c0 = Fp6Gadget::<P, ConstraintF>::zero(cs.ns(|| "c0"))?;
|
||||
let c1 = Fp6Gadget::<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 = Fp6Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
|
||||
let c1 = Fp6Gadget::<P, ConstraintF>::zero(cs.ns(|| "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(cs.ns(|| "c0"), &other.c0)?;
|
||||
let c1 = self.c1.add(cs.ns(|| "c1"), &other.c1)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c0.add_in_place(cs.ns(|| "c0"), &other.c0)?;
|
||||
self.c1.add_in_place(cs.ns(|| "c1"), &other.c1)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let c0 = self.c0.sub(cs.ns(|| "c0"), &other.c0)?;
|
||||
let c1 = self.c1.sub(cs.ns(|| "c1"), &other.c1)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c0.sub_in_place(cs.ns(|| "c0"), &other.c0)?;
|
||||
self.c1.sub_in_place(cs.ns(|| "c1"), &other.c1)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
let c0 = self.c0.negate(cs.ns(|| "c0"))?;
|
||||
let c1 = self.c1.negate(cs.ns(|| "c1"))?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c0.negate_in_place(cs.ns(|| "c0"))?;
|
||||
self.c1.negate_in_place(cs.ns(|| "c1"))?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
// Karatsuba multiplication:
|
||||
// v0 = A.c0 * B.c0
|
||||
// v1 = A.c1 * B.c1
|
||||
// result.c0 = v0 + non_residue * v1
|
||||
// result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
//
|
||||
// Enforced with 3 Fp3_mul_gadget's that ensure that:
|
||||
// A.c1 * B.c1 = v1
|
||||
// A.c0 * B.c0 = v0
|
||||
// (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + v0 + v1
|
||||
|
||||
let v0 = self.c0.mul(cs.ns(|| "v0"), &other.c0)?;
|
||||
let v1 = self.c1.mul(cs.ns(|| "v1"), &other.c1)?;
|
||||
let c0 = {
|
||||
let non_residue_times_v1 =
|
||||
Self::mul_fp6_by_nonresidue(cs.ns(|| "first mul_by_nr"), &v1)?;
|
||||
v0.add(cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)?
|
||||
};
|
||||
let c1 = {
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?;
|
||||
let a0_plus_a1_times_b0_plus_b1 =
|
||||
a0_plus_a1.mul(&mut cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?;
|
||||
a0_plus_a1_times_b0_plus_b1
|
||||
.sub(cs.ns(|| "res - v0"), &v0)?
|
||||
.sub(cs.ns(|| "res - v0 - v1"), &v1)?
|
||||
};
|
||||
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
fn square<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
// From Libsnark/fp2_gadget.tcc
|
||||
// Complex multiplication for Fp2:
|
||||
// v0 = A.c0 * A.c1
|
||||
// result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 +
|
||||
// non_residue) * v0 result.c1 = 2 * v0
|
||||
// Enforced with 2 constraints:
|
||||
// (2*A.c0) * A.c1 = result.c1
|
||||
// (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1
|
||||
// + non_residue)/2 Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
||||
let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?;
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
||||
let non_residue_a1 = Self::mul_fp6_by_nonresidue(cs.ns(|| "non_residue * a1"), &self.c1)?;
|
||||
let a0_plus_non_residue_a1 = self
|
||||
.c0
|
||||
.add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_a1)?;
|
||||
let one_plus_non_residue_v0 =
|
||||
Self::mul_fp6_by_nonresidue(cs.ns(|| "non_residue * v0"), &v0)?
|
||||
.add(cs.ns(|| "plus v0"), &v0)?;
|
||||
|
||||
let c0 = a0_plus_a1
|
||||
.mul(
|
||||
cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"),
|
||||
&a0_plus_non_residue_a1,
|
||||
)?
|
||||
.sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?;
|
||||
|
||||
v0.double_in_place(cs.ns(|| "2v0"))?;
|
||||
let c1 = v0;
|
||||
|
||||
Ok(Self {
|
||||
c0,
|
||||
c1,
|
||||
_params: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Fp12<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)?;
|
||||
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
other: &Fp12<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,
|
||||
other: &Fp12<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) = (other.c0, other.c1);
|
||||
let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?;
|
||||
let mut v1 = Self::mul_fp6_by_nonresidue(&mut cs.ns(|| "v1"), a1)?;
|
||||
let beta_v1 = v1.mul_by_constant_in_place(&mut cs.ns(|| "beta * v1"), &b1)?;
|
||||
|
||||
v0.add_in_place(&mut cs.ns(|| "c0"), beta_v1)?;
|
||||
let c0 = v0;
|
||||
|
||||
let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?;
|
||||
let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?;
|
||||
a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?;
|
||||
let c1 = a0b1;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
cs: CS,
|
||||
power: usize,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut res = self.clone();
|
||||
res.frobenius_map_in_place(cs, power)?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
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
|
||||
.c0
|
||||
.mul_by_constant_in_place(cs.ns(|| "mul1"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
|
||||
self.c1
|
||||
.c1
|
||||
.mul_by_constant_in_place(cs.ns(|| "mul2"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
|
||||
self.c1
|
||||
.c2
|
||||
.mul_by_constant_in_place(cs.ns(|| "mul3"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn 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 Fp2 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 = Fp6::<P::Fp6Params>::one();
|
||||
let rhs = Self::mul_fp6_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 Fp2:
|
||||
// v0 = A.c0 * B.c0
|
||||
// v1 = A.c1 * B.c1
|
||||
// result.c0 = v0 + non_residue * v1
|
||||
// result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
// Enforced with 3 constraints:
|
||||
// A.c1 * B.c1 = v1
|
||||
// A.c0 * B.c0 = result.c0 - non_residue * v1
|
||||
// (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
|
||||
// Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
let mul_cs = &mut cs.ns(|| "mul");
|
||||
|
||||
// Compute v1
|
||||
let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
|
||||
|
||||
// Perform second check
|
||||
let non_residue_times_v1 = Self::mul_fp6_by_nonresidue(mul_cs.ns(|| "nr * v1"), &v1)?;
|
||||
let rhs = result
|
||||
.c0
|
||||
.sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?;
|
||||
self.c0
|
||||
.mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?;
|
||||
|
||||
// Last check
|
||||
let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
|
||||
let one_minus_non_residue_v1 =
|
||||
v1.sub(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?;
|
||||
|
||||
let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result
|
||||
.c1
|
||||
.add(mul_cs.ns(|| "c1 + c0"), &result.c0)?
|
||||
.add(mul_cs.ns(|| "rest of stuff"), &one_minus_non_residue_v1)?;
|
||||
|
||||
a0_plus_a1.mul_equals(
|
||||
mul_cs.ns(|| "third check"),
|
||||
&b0_plus_b1,
|
||||
&result_c1_plus_result_c0_plus_one_minus_non_residue_v1,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cost_of_mul() -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> PartialEq for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.c0 == other.c0 && self.c1 == other.c1
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> Eq for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::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 * <Fp6Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::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 * <Fp6Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::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(&mut cs)?;
|
||||
let mut c1 = self.c1.to_bits(cs)?;
|
||||
c0.append(&mut c1);
|
||||
Ok(c0)
|
||||
}
|
||||
|
||||
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bits_strict(&mut cs)?;
|
||||
let mut c1 = self.c1.to_bits_strict(cs)?;
|
||||
c0.append(&mut c1);
|
||||
Ok(c0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::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_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bytes_strict(cs.ns(|| "c0"))?;
|
||||
let mut c1 = self.c1.to_bytes_strict(cs.ns(|| "c1"))?;
|
||||
c0.append(&mut c1);
|
||||
Ok(c0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> Clone for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(self.c0.clone(), self.c1.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::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 = Fp6Gadget::<P, ConstraintF>::conditionally_select(
|
||||
&mut cs.ns(|| "c0"),
|
||||
cond,
|
||||
&first.c0,
|
||||
&second.c0,
|
||||
)?;
|
||||
let c1 = Fp6Gadget::<P, ConstraintF>::conditionally_select(
|
||||
&mut cs.ns(|| "c1"),
|
||||
cond,
|
||||
&first.c1,
|
||||
&second.c1,
|
||||
)?;
|
||||
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
2 * <Fp6Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
type TableConstant = Fp12<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 = Fp6Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
|
||||
let c1 = Fp6Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
2 * <Fp6Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> AllocGadget<Fp12<P>, ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp12Parameters,
|
||||
<P::Fp6Params as Fp6Parameters>::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<Fp12<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 = Fp6Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
||||
let c1 = Fp6Gadget::<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<Fp12<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 = Fp6Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
||||
let c1 = Fp6Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
}
|
||||
627
r1cs-std/src/fields/fp2.rs
Normal file
627
r1cs-std/src/fields/fp2.rs
Normal file
@@ -0,0 +1,627 @@
|
||||
use algebra::{
|
||||
fields::{Fp2, Fp2Parameters},
|
||||
Field, PrimeField,
|
||||
};
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use crate::fields::fp::FpGadget;
|
||||
use crate::prelude::*;
|
||||
use crate::{Assignment, ConstraintVar};
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = "P: Fp2Parameters, ConstraintF: PrimeField"))]
|
||||
#[must_use]
|
||||
pub struct Fp2Gadget<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> {
|
||||
pub c0: FpGadget<ConstraintF>,
|
||||
pub c1: FpGadget<ConstraintF>,
|
||||
#[derivative(Debug = "ignore")]
|
||||
_params: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Fp2Gadget<P, ConstraintF> {
|
||||
pub fn new(c0: FpGadget<ConstraintF>, c1: FpGadget<ConstraintF>) -> Self {
|
||||
Self {
|
||||
c0,
|
||||
c1,
|
||||
_params: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply a FpGadget by quadratic nonresidue P::NONRESIDUE.
|
||||
#[inline]
|
||||
pub fn mul_fp_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
||||
cs: CS,
|
||||
fe: &FpGadget<ConstraintF>,
|
||||
) -> Result<FpGadget<ConstraintF>, SynthesisError> {
|
||||
fe.mul_by_constant(cs, &P::NONRESIDUE)
|
||||
}
|
||||
|
||||
/// Multiply a Fp2Gadget by an element of fp.
|
||||
#[inline]
|
||||
pub fn mul_by_fp_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
fe: &P::Fp,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c0.mul_by_constant_in_place(cs.ns(|| "c0"), fe)?;
|
||||
self.c1.mul_by_constant_in_place(cs.ns(|| "c1"), fe)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Multiply a Fp2Gadget by an element of fp.
|
||||
#[inline]
|
||||
pub fn mul_by_fp_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
cs: CS,
|
||||
fe: &P::Fp,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut result = self.clone();
|
||||
result.mul_by_fp_constant_in_place(cs, fe)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> FieldGadget<Fp2<P>, ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
type Variable = (ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>);
|
||||
|
||||
#[inline]
|
||||
fn get_value(&self) -> Option<Fp2<P>> {
|
||||
match (self.c0.value, self.c1.value) {
|
||||
(Some(c0), Some(c1)) => Some(Fp2::new(c0, c1)),
|
||||
(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_variable(&self) -> Self::Variable {
|
||||
(
|
||||
self.c0.get_variable().clone(),
|
||||
self.c1.get_variable().clone(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
let c0 = FpGadget::zero(cs.ns(|| "c0"))?;
|
||||
let c1 = FpGadget::zero(cs.ns(|| "c1"))?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
let c0 = FpGadget::one(cs.ns(|| "c0"))?;
|
||||
let c1 = FpGadget::zero(cs.ns(|| "c1"))?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?;
|
||||
let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?;
|
||||
let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn double<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
let mut result = self.clone();
|
||||
result.double_in_place(cs)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c0.double_in_place(&mut cs.ns(|| "double c0"))?;
|
||||
self.c1.double_in_place(&mut cs.ns(|| "double c1"))?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
let mut result = self.clone();
|
||||
result.negate_in_place(cs)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?;
|
||||
self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
// Karatsuba multiplication for Fp2:
|
||||
// v0 = A.c0 * B.c0
|
||||
// v1 = A.c1 * B.c1
|
||||
// result.c0 = v0 + non_residue * v1
|
||||
// result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
// Enforced with 3 constraints:
|
||||
// A.c1 * B.c1 = v1
|
||||
// A.c0 * B.c0 = result.c0 - non_residue * v1
|
||||
// (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
|
||||
// Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
let mul_cs = &mut cs.ns(|| "mul");
|
||||
|
||||
let v0 = self.c0.mul(mul_cs.ns(|| "v0"), &other.c0)?;
|
||||
let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
|
||||
let c0 = {
|
||||
let non_residue_times_v1 =
|
||||
v1.mul_by_constant(mul_cs.ns(|| "non_residue * v0"), &P::NONRESIDUE)?;
|
||||
v0.add(mul_cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)?
|
||||
};
|
||||
let c1 = {
|
||||
let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
|
||||
let a0_plus_a1_times_b0_plus_b1 =
|
||||
a0_plus_a1.mul(&mut mul_cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?;
|
||||
a0_plus_a1_times_b0_plus_b1
|
||||
.sub(mul_cs.ns(|| "res - v0"), &v0)?
|
||||
.sub(mul_cs.ns(|| "res - v0 - v1"), &v1)?
|
||||
};
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn square<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
// From Libsnark/fp2_gadget.tcc
|
||||
// Complex multiplication for Fp2:
|
||||
// v0 = A.c0 * A.c1
|
||||
// result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 +
|
||||
// non_residue) * v0 result.c1 = 2 * v0
|
||||
// Enforced with 2 constraints:
|
||||
// (2*A.c0) * A.c1 = result.c1
|
||||
// (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1
|
||||
// + non_residue)/2 Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
||||
let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?;
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
||||
let non_residue_c1 = self
|
||||
.c1
|
||||
.mul_by_constant(cs.ns(|| "non_residue * a1"), &P::NONRESIDUE)?;
|
||||
let a0_plus_non_residue_c1 = self
|
||||
.c0
|
||||
.add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_c1)?;
|
||||
let one_plus_non_residue_v0 = v0.mul_by_constant(
|
||||
cs.ns(|| "1 + non_residue * v0"),
|
||||
&(P::Fp::one() + &P::NONRESIDUE),
|
||||
)?;
|
||||
|
||||
let c0 = a0_plus_a1
|
||||
.mul(
|
||||
cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"),
|
||||
&a0_plus_non_residue_c1,
|
||||
)?
|
||||
.sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?;
|
||||
|
||||
v0.double_in_place(cs.ns(|| "2v0"))?;
|
||||
let c1 = v0;
|
||||
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn square_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
// From Libsnark/fp2_gadget.tcc
|
||||
// Complex multiplication for Fp2:
|
||||
// v0 = A.c0 * A.c1
|
||||
// result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 +
|
||||
// non_residue) * v0 result.c1 = 2 * v0
|
||||
// Enforced with 2 constraints:
|
||||
// (2*A.c0) * A.c1 = result.c1
|
||||
// (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1
|
||||
// + non_residue)/2 Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
||||
let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?;
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
||||
let _ = self
|
||||
.c1
|
||||
.mul_by_constant_in_place(cs.ns(|| "non_residue * a1"), &P::NONRESIDUE)?;
|
||||
let a0_plus_non_residue_c1 = self.c0.add(cs.ns(|| "a0 + non_residue * a1"), &self.c1)?;
|
||||
let one_plus_non_residue_v0 = v0.mul_by_constant(
|
||||
cs.ns(|| "1 + non_residue * v0"),
|
||||
&(P::Fp::one() + &P::NONRESIDUE),
|
||||
)?;
|
||||
|
||||
self.c0 = a0_plus_a1
|
||||
.mul(
|
||||
cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"),
|
||||
&a0_plus_non_residue_c1,
|
||||
)?
|
||||
.sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?;
|
||||
|
||||
v0.double_in_place(cs.ns(|| "2v0"))?;
|
||||
self.c1 = v0;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[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 Fp2 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 mut 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 = P::Fp::one();
|
||||
let rhs = v1
|
||||
.mul_by_constant_in_place(cs.ns(|| "(1 - nonresidue) * v1"), &(one - &P::NONRESIDUE))?
|
||||
.add_constant_in_place(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 Fp2:
|
||||
// v0 = A.c0 * B.c0
|
||||
// v1 = A.c1 * B.c1
|
||||
// result.c0 = v0 + non_residue * v1
|
||||
// result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
// Enforced with 3 constraints:
|
||||
// A.c1 * B.c1 = v1
|
||||
// A.c0 * B.c0 = result.c0 - non_residue * v1
|
||||
// (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
|
||||
// Reference:
|
||||
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
let mul_cs = &mut cs.ns(|| "mul");
|
||||
|
||||
// Compute v1
|
||||
let mut v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
|
||||
|
||||
// Perform second check
|
||||
let non_residue_times_v1 =
|
||||
v1.mul_by_constant(mul_cs.ns(|| "non_residue * v0"), &P::NONRESIDUE)?;
|
||||
let rhs = result
|
||||
.c0
|
||||
.sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?;
|
||||
self.c0
|
||||
.mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?;
|
||||
|
||||
// Last check
|
||||
let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
|
||||
let one_minus_non_residue_v1 =
|
||||
v1.sub_in_place(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?;
|
||||
|
||||
let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result
|
||||
.c1
|
||||
.add(mul_cs.ns(|| "c1 + c0"), &result.c0)?
|
||||
.add(mul_cs.ns(|| "rest of stuff"), one_minus_non_residue_v1)?;
|
||||
|
||||
a0_plus_a1.mul_equals(
|
||||
mul_cs.ns(|| "third check"),
|
||||
&b0_plus_b1,
|
||||
&result_c1_plus_result_c0_plus_one_minus_non_residue_v1,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
cs: CS,
|
||||
power: usize,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut result = self.clone();
|
||||
let _ = result.frobenius_map_in_place(cs, power)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
power: usize,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c1
|
||||
.mul_by_constant_in_place(cs, &P::FROBENIUS_COEFF_FP2_C1[power % 2])?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
cs: CS,
|
||||
other: &Fp2<P>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut result = self.clone();
|
||||
let _ = result.add_constant_in_place(cs, other)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
mut cs: CS,
|
||||
other: &Fp2<P>,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?;
|
||||
self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
fe: &Fp2<P>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
// Karatsuba multiplication (see mul above).
|
||||
// Doesn't need any constraints; returns linear combinations of
|
||||
// `self`'s variables.
|
||||
//
|
||||
// (The operations below are guaranteed to return linear combinations)
|
||||
let (a0, a1) = (&self.c0, &self.c1);
|
||||
let (b0, b1) = (fe.c0, fe.c1);
|
||||
let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?;
|
||||
let beta_v1 = a1.mul_by_constant(&mut cs.ns(|| "v1"), &(b1 * &P::NONRESIDUE))?;
|
||||
|
||||
v0.add_in_place(&mut cs.ns(|| "c0"), &beta_v1)?;
|
||||
let c0 = v0;
|
||||
|
||||
let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?;
|
||||
let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?;
|
||||
a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?;
|
||||
let c1 = a0b1;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
fn cost_of_mul() -> usize {
|
||||
3
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> PartialEq for Fp2Gadget<P, ConstraintF> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.c0 == other.c0 && self.c1 == other.c1
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Eq for Fp2Gadget<P, ConstraintF> {}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
#[inline]
|
||||
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
condition: &Boolean,
|
||||
) -> Result<(), SynthesisError> {
|
||||
self.c0
|
||||
.conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?;
|
||||
self.c1
|
||||
.conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
#[inline]
|
||||
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<(), SynthesisError> {
|
||||
self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?;
|
||||
self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bits(&mut cs)?;
|
||||
let mut c1 = self.c1.to_bits(cs)?;
|
||||
c0.append(&mut c1);
|
||||
Ok(c0)
|
||||
}
|
||||
|
||||
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bits_strict(&mut cs)?;
|
||||
let mut c1 = self.c1.to_bits_strict(cs)?;
|
||||
c0.append(&mut c1);
|
||||
Ok(c0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?;
|
||||
let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?;
|
||||
c0.append(&mut c1);
|
||||
Ok(c0)
|
||||
}
|
||||
|
||||
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bytes_strict(cs.ns(|| "c0"))?;
|
||||
let mut c1 = self.c1.to_bytes_strict(cs.ns(|| "c1"))?;
|
||||
c0.append(&mut c1);
|
||||
Ok(c0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Clone for Fp2Gadget<P, ConstraintF> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
c0: self.c0.clone(),
|
||||
c1: self.c1.clone(),
|
||||
_params: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
#[inline]
|
||||
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
||||
mut cs: CS,
|
||||
cond: &Boolean,
|
||||
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)?;
|
||||
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
type TableConstant = Fp2<P>;
|
||||
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
||||
mut cs: CS,
|
||||
b: &[Boolean],
|
||||
c: &[Self::TableConstant],
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
||||
let c0 = FpGadget::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
|
||||
let c1 = FpGadget::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
2 * <FpGadget<ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> AllocGadget<Fp2<P>, ConstraintF> for Fp2Gadget<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<Fp2<P>>,
|
||||
{
|
||||
let (c0, c1) = match value_gen() {
|
||||
Ok(fe) => {
|
||||
let fe = *fe.borrow();
|
||||
(Ok(fe.c0), Ok(fe.c1))
|
||||
},
|
||||
Err(_) => (
|
||||
Err(SynthesisError::AssignmentMissing),
|
||||
Err(SynthesisError::AssignmentMissing),
|
||||
),
|
||||
};
|
||||
|
||||
let c0 = FpGadget::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
||||
let c1 = FpGadget::alloc(&mut cs.ns(|| "c1"), || c1)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
mut cs: CS,
|
||||
value_gen: F,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
T: Borrow<Fp2<P>>,
|
||||
{
|
||||
let (c0, c1) = match value_gen() {
|
||||
Ok(fe) => {
|
||||
let fe = *fe.borrow();
|
||||
(Ok(fe.c0), Ok(fe.c1))
|
||||
},
|
||||
Err(_) => (
|
||||
Err(SynthesisError::AssignmentMissing),
|
||||
Err(SynthesisError::AssignmentMissing),
|
||||
),
|
||||
};
|
||||
|
||||
let c0 = FpGadget::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
||||
let c1 = FpGadget::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
||||
Ok(Self::new(c0, c1))
|
||||
}
|
||||
}
|
||||
940
r1cs-std/src/fields/fp6_3over2.rs
Normal file
940
r1cs-std/src/fields/fp6_3over2.rs
Normal file
@@ -0,0 +1,940 @@
|
||||
use algebra::{
|
||||
fields::{
|
||||
fp6_3over2::{Fp6, Fp6Parameters},
|
||||
Field, Fp2Parameters,
|
||||
},
|
||||
PrimeField,
|
||||
};
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::ConstraintVar;
|
||||
use crate::Assignment;
|
||||
|
||||
type Fp2Gadget<P, ConstraintF> = super::fp2::Fp2Gadget<<P as Fp6Parameters>::Fp2Params, ConstraintF>;
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = "ConstraintF: PrimeField"))]
|
||||
#[must_use]
|
||||
pub struct Fp6Gadget<P, ConstraintF: PrimeField>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
pub c0: Fp2Gadget<P, ConstraintF>,
|
||||
pub c1: Fp2Gadget<P, ConstraintF>,
|
||||
pub c2: Fp2Gadget<P, ConstraintF>,
|
||||
#[derivative(Debug = "ignore")]
|
||||
_params: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn new(c0: Fp2Gadget<P, ConstraintF>, c1: Fp2Gadget<P, ConstraintF>, c2: Fp2Gadget<P, ConstraintF>) -> Self {
|
||||
Self {
|
||||
c0,
|
||||
c1,
|
||||
c2,
|
||||
_params: PhantomData,
|
||||
}
|
||||
}
|
||||
/// Multiply a Fp2Gadget by cubic 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> {
|
||||
fe.mul_by_constant(cs, &P::NONRESIDUE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mul_by_0_c1_0<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
c1: &Fp2Gadget<P, ConstraintF>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
// Karatsuba multiplication
|
||||
// v0 = a0 * b0 = 0
|
||||
|
||||
// v1 = a1 * b1
|
||||
let v1 = self.c1.mul(cs.ns(|| "first mul"), c1)?;
|
||||
|
||||
// v2 = a2 * b2 = 0
|
||||
|
||||
let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?;
|
||||
let b1_plus_b2 = c1.clone();
|
||||
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
||||
// c0 = (NONRESIDUE * ((a1 + a2)*(b1 + b2) - v1 - v2)) + v0
|
||||
// = NONRESIDUE * ((a1 + a2) * b1 - v1)
|
||||
let c0 = a1_plus_a2
|
||||
.mul(cs.ns(|| "second mul"), &b1_plus_b2)?
|
||||
.sub(cs.ns(|| "first sub"), &v1)?
|
||||
.mul_by_constant(cs.ns(|| "mul_by_nonresidue"), &P::NONRESIDUE)?;
|
||||
|
||||
// c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + NONRESIDUE * v2
|
||||
// = (a0 + a1) * b1 - v1
|
||||
let c1 = a0_plus_a1
|
||||
.mul(cs.ns(|| "third mul"), &c1)?
|
||||
.sub(cs.ns(|| "second sub"), &v1)?;
|
||||
// c2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1
|
||||
// = v1
|
||||
let c2 = v1;
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
|
||||
// #[inline]
|
||||
pub fn mul_by_c0_c1_0<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
c0: &Fp2Gadget<P, ConstraintF>,
|
||||
c1: &Fp2Gadget<P, ConstraintF>,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let v0 = self.c0.mul(cs.ns(|| "v0"), c0)?;
|
||||
let v1 = self.c1.mul(cs.ns(|| "v1"), c1)?;
|
||||
// v2 = 0.
|
||||
|
||||
let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?;
|
||||
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?;
|
||||
|
||||
let b1_plus_b2 = c1.clone();
|
||||
let b0_plus_b1 = c0.add(cs.ns(|| "b0 + b1"), &c1)?;
|
||||
let b0_plus_b2 = c0.clone();
|
||||
|
||||
let c0 = {
|
||||
let cs = &mut cs.ns(|| "c0");
|
||||
a1_plus_a2
|
||||
.mul(cs.ns(|| "(a1 + a2) * (b1 + b2)"), &b1_plus_b2)?
|
||||
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
.mul_by_constant(cs.ns(|| "First mul_by_nonresidue"), &P::NONRESIDUE)?
|
||||
.add(cs.ns(|| "add v0"), &v0)?
|
||||
};
|
||||
|
||||
let c1 = {
|
||||
let cs = &mut cs.ns(|| "c1");
|
||||
a0_plus_a1
|
||||
.mul(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?
|
||||
.sub(cs.ns(|| "sub v0"), &v0)?
|
||||
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
};
|
||||
|
||||
let c2 = {
|
||||
a0_plus_a2
|
||||
.mul(cs.ns(|| "(a0 + a2) * (b0 + b2)"), &b0_plus_b2)?
|
||||
.sub(cs.ns(|| "sub v0"), &v0)?
|
||||
.add(cs.ns(|| "add v1"), &v1)?
|
||||
};
|
||||
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> FieldGadget<Fp6<P>, ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
type Variable = (
|
||||
(ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
|
||||
(ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
|
||||
(ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
|
||||
);
|
||||
|
||||
#[inline]
|
||||
fn get_value(&self) -> Option<Fp6<P>> {
|
||||
match (
|
||||
self.c0.get_value(),
|
||||
self.c1.get_value(),
|
||||
self.c2.get_value(),
|
||||
) {
|
||||
(Some(c0), Some(c1), Some(c2)) => Some(Fp6::new(c0, c1, c2)),
|
||||
(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_variable(&self) -> Self::Variable {
|
||||
(
|
||||
self.c0.get_variable(),
|
||||
self.c1.get_variable(),
|
||||
self.c2.get_variable(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn zero<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"))?;
|
||||
let c2 = Fp2Gadget::<P, 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 = Fp2Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
|
||||
let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
||||
let c2 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "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-Cool-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::Fp2Params as Fp2Parameters>::Fp::one().double();
|
||||
let six = two.double() + &two;
|
||||
let mut two_and_six = [two, six];
|
||||
algebra::fields::batch_inversion(&mut two_and_six);
|
||||
let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
|
||||
|
||||
let half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?;
|
||||
let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?;
|
||||
let one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_six"), &six_inverse)?;
|
||||
let one_sixth_v3 = v3.mul_by_fp_constant(cs.ns(|| "v3_by_six"), &six_inverse)?;
|
||||
let two_v4 = v4.double(cs.ns(|| "2 * v4"))?;
|
||||
|
||||
// c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4)
|
||||
let c0 = {
|
||||
let c0_cs = &mut cs.ns(|| "c0");
|
||||
|
||||
// No constraints, only get a linear combination back.
|
||||
let temp = half_v0
|
||||
.sub(c0_cs.ns(|| "sub1"), &half_v1)?
|
||||
.sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)?
|
||||
.add(c0_cs.ns(|| "add3"), &one_sixth_v3)?
|
||||
.sub(c0_cs.ns(|| "sub4"), &two_v4)?;
|
||||
let non_residue_times_inner =
|
||||
temp.mul_by_constant(&mut c0_cs.ns(|| "mul5"), &P::NONRESIDUE)?;
|
||||
v0.add(c0_cs.ns(|| "add6"), &non_residue_times_inner)?
|
||||
};
|
||||
|
||||
// −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4
|
||||
let c1 = {
|
||||
let c1_cs = &mut cs.ns(|| "c1");
|
||||
let one_third_v2 = one_sixth_v2.double(&mut c1_cs.ns(|| "v2_by_3"))?;
|
||||
let non_residue_v4 =
|
||||
v4.mul_by_constant(&mut c1_cs.ns(|| "mul_by_beta"), &P::NONRESIDUE)?;
|
||||
|
||||
let result = half_v0
|
||||
.negate(c1_cs.ns(|| "neg1"))?
|
||||
.add(c1_cs.ns(|| "add2"), &v1)?
|
||||
.sub(c1_cs.ns(|| "sub3"), &one_third_v2)?
|
||||
.sub(c1_cs.ns(|| "sub4"), &one_sixth_v3)?
|
||||
.add(c1_cs.ns(|| "sub5"), &two_v4)?
|
||||
.add(c1_cs.ns(|| "sub6"), &non_residue_v4)?;
|
||||
result
|
||||
};
|
||||
|
||||
// -v0 + (1/2)v1 + (1/2)v2 −v4
|
||||
let c2 = {
|
||||
let c2_cs = &mut cs.ns(|| "c2");
|
||||
let half_v2 = v2.mul_by_fp_constant(&mut c2_cs.ns(|| "mul1"), &two_inverse)?;
|
||||
let result = half_v1
|
||||
.add(c2_cs.ns(|| "add1"), &half_v2)?
|
||||
.sub(c2_cs.ns(|| "sub1"), &v4)?
|
||||
.sub(c2_cs.ns(|| "sub2"), &v0)?;
|
||||
result
|
||||
};
|
||||
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
|
||||
/// Use the Toom-Cook-3x method to compute multiplication.
|
||||
#[inline]
|
||||
fn square<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
// Uses Toom-Cool-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::Fp2Params as Fp2Parameters>::Fp::one().double();
|
||||
let six = two.double() + &two;
|
||||
let mut two_and_six = [two, six];
|
||||
algebra::fields::batch_inversion(&mut two_and_six);
|
||||
let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
|
||||
|
||||
let half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?;
|
||||
let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?;
|
||||
let one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "one_sixth_v2"), &six_inverse)?;
|
||||
let one_sixth_v3 = v3.mul_by_fp_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_fp_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 constaints, 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: &Fp6<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: &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)?;
|
||||
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: &Fp6<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::Fp2Params as Fp2Parameters>::Fp::one().double();
|
||||
let six = two.double() + &two;
|
||||
let mut two_and_six = [two, six];
|
||||
algebra::fields::batch_inversion(&mut two_and_six);
|
||||
let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
|
||||
|
||||
let mut half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?;
|
||||
let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?;
|
||||
let mut one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_6"), &six_inverse)?;
|
||||
let one_sixth_v3 = v3.mul_by_fp_constant_in_place(cs.ns(|| "v3_by_6"), &six_inverse)?;
|
||||
let two_v4 = v4.double(cs.ns(|| "2v4"))?;
|
||||
|
||||
// c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4)
|
||||
let c0 = {
|
||||
let mut c0_cs = cs.ns(|| "c0");
|
||||
|
||||
// No constraints, only get a linear combination back.
|
||||
let mut inner = half_v0
|
||||
.sub(c0_cs.ns(|| "sub1"), &half_v1)?
|
||||
.sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)?
|
||||
.add(c0_cs.ns(|| "add3"), &one_sixth_v3)?
|
||||
.sub(c0_cs.ns(|| "sub4"), &two_v4)?;
|
||||
let non_residue_times_inner =
|
||||
inner.mul_by_constant_in_place(&mut c0_cs, &P::NONRESIDUE)?;
|
||||
v0.add(c0_cs.ns(|| "add5"), non_residue_times_inner)?
|
||||
};
|
||||
|
||||
// −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4
|
||||
let c1 = {
|
||||
let mut c1_cs = cs.ns(|| "c1");
|
||||
let one_third_v2 = one_sixth_v2.double_in_place(c1_cs.ns(|| "double1"))?;
|
||||
let non_residue_v4 =
|
||||
v4.mul_by_constant(c1_cs.ns(|| "mul_by_const1"), &P::NONRESIDUE)?;
|
||||
|
||||
half_v0
|
||||
.negate_in_place(c1_cs.ns(|| "neg1"))?
|
||||
.add(c1_cs.ns(|| "add1"), &v1)?
|
||||
.sub(c1_cs.ns(|| "sub2"), one_third_v2)?
|
||||
.sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)?
|
||||
.add(c1_cs.ns(|| "add4"), &two_v4)?
|
||||
.add(c1_cs.ns(|| "add5"), &non_residue_v4)?
|
||||
};
|
||||
|
||||
// -v0 + (1/2)v1 + (1/2)v2 −v4
|
||||
let c2 = {
|
||||
let mut c2_cs = cs.ns(|| "c2");
|
||||
let half_v2 = v2.mul_by_fp_constant_in_place(c2_cs.ns(|| "half_v2"), &two_inverse)?;
|
||||
half_v1
|
||||
.add(c2_cs.ns(|| "add1"), half_v2)?
|
||||
.sub(c2_cs.ns(|| "sub2"), &v4)?
|
||||
.sub(c2_cs.ns(|| "sub3"), &v0)?
|
||||
};
|
||||
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
|
||||
fn frobenius_map<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.c0.frobenius_map_in_place(&mut cs.ns(|| "c0"), power)?;
|
||||
self.c1.frobenius_map_in_place(&mut cs.ns(|| "c1"), power)?;
|
||||
self.c2.frobenius_map_in_place(&mut cs.ns(|| "c2"), power)?;
|
||||
|
||||
self.c1.mul_by_constant_in_place(
|
||||
cs.ns(|| "c1_power"),
|
||||
&P::FROBENIUS_COEFF_FP6_C1[power % 6],
|
||||
)?;
|
||||
self.c2.mul_by_constant_in_place(
|
||||
cs.ns(|| "c2_power"),
|
||||
&P::FROBENIUS_COEFF_FP6_C2[power % 6],
|
||||
)?;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn cost_of_mul() -> usize {
|
||||
5 * Fp2Gadget::<P, ConstraintF>::cost_of_mul()
|
||||
}
|
||||
|
||||
fn cost_of_inv() -> usize {
|
||||
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> PartialEq for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.c0 == other.c0 && self.c1 == other.c1 && self.c2 == other.c2
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> Eq for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
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)?;
|
||||
self.c2
|
||||
.conditional_enforce_equal(&mut cs.ns(|| "c2"), &other.c2, condition)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
3 * <Fp2Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
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)?;
|
||||
self.c2.enforce_not_equal(&mut cs.ns(|| "c2"), &other.c2)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
3 * <Fp2Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
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(&mut cs)?;
|
||||
let mut c1 = self.c1.to_bits(&mut cs)?;
|
||||
let mut c2 = self.c2.to_bits(cs)?;
|
||||
|
||||
c0.append(&mut c1);
|
||||
c0.append(&mut c2);
|
||||
|
||||
Ok(c0)
|
||||
}
|
||||
|
||||
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
let mut c0 = self.c0.to_bits_strict(&mut cs)?;
|
||||
let mut c1 = self.c1.to_bits_strict(&mut cs)?;
|
||||
let mut c2 = self.c2.to_bits_strict(cs)?;
|
||||
|
||||
c0.append(&mut c1);
|
||||
c0.append(&mut c2);
|
||||
|
||||
Ok(c0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
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"))?;
|
||||
let mut c2 = self.c2.to_bytes(cs.ns(|| "c2"))?;
|
||||
|
||||
c0.append(&mut c1);
|
||||
c0.append(&mut c2);
|
||||
|
||||
Ok(c0)
|
||||
}
|
||||
|
||||
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
cs: CS,
|
||||
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
self.to_bytes(cs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> Clone for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(self.c0.clone(), self.c1.clone(), self.c2.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
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,
|
||||
)?;
|
||||
let c2 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
|
||||
&mut cs.ns(|| "c2"),
|
||||
cond,
|
||||
&first.c2,
|
||||
&second.c2,
|
||||
)?;
|
||||
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
3 * <Fp2Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
P::Fp2Params: Fp2Parameters<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 c2s = c.iter().map(|f| f.c2).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)?;
|
||||
let c2 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c2"), b, &c2s)?;
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
3 * <Fp2Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, ConstraintF: PrimeField> AllocGadget<Fp6<P>, ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
where
|
||||
P: Fp6Parameters,
|
||||
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<Fp6<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 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
||||
let c1 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
|
||||
let c2 = Fp2Gadget::<P, 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<Fp6<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 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
||||
let c1 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
||||
let c2 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c2"), || c2)?;
|
||||
Ok(Self::new(c0, c1, c2))
|
||||
}
|
||||
}
|
||||
6
r1cs-std/src/fields/jubjub.rs
Normal file
6
r1cs-std/src/fields/jubjub.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use algebra::fields::jubjub::fq::Fq;
|
||||
|
||||
use crate::fields::fp::FpGadget;
|
||||
|
||||
// JubJub Fq uses BLS12-381 Fr.
|
||||
pub type FqGadget = FpGadget<Fq>;
|
||||
512
r1cs-std/src/fields/mod.rs
Normal file
512
r1cs-std/src/fields/mod.rs
Normal file
@@ -0,0 +1,512 @@
|
||||
// use std::ops::{Mul, MulAssign};
|
||||
use algebra::Field;
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub mod fp;
|
||||
pub mod fp12;
|
||||
pub mod fp2;
|
||||
pub mod fp6_3over2;
|
||||
|
||||
pub mod bls12_377;
|
||||
pub mod edwards_bls12;
|
||||
pub mod edwards_sw6;
|
||||
pub mod jubjub;
|
||||
|
||||
pub trait FieldGadget<F: Field, ConstraintF: Field>:
|
||||
Sized
|
||||
+ Clone
|
||||
+ EqGadget<ConstraintF>
|
||||
+ NEqGadget<ConstraintF>
|
||||
+ ConditionalEqGadget<ConstraintF>
|
||||
+ ToBitsGadget<ConstraintF>
|
||||
+ AllocGadget<F, ConstraintF>
|
||||
+ ToBytesGadget<ConstraintF>
|
||||
+ CondSelectGadget<ConstraintF>
|
||||
+ TwoBitLookupGadget<ConstraintF, TableConstant = F>
|
||||
+ Debug
|
||||
{
|
||||
type Variable: Clone + Debug;
|
||||
|
||||
fn get_value(&self) -> Option<F>;
|
||||
|
||||
fn get_variable(&self) -> Self::Variable;
|
||||
|
||||
fn zero<CS: ConstraintSystem<ConstraintF>>(_: CS) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn one<CS: ConstraintSystem<ConstraintF>>(_: CS) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn add<CS: ConstraintSystem<ConstraintF>>(&self, _: CS, _: &Self) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn add_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.add(cs, other)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn double<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
self.add(cs, &self)
|
||||
}
|
||||
|
||||
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.double(cs)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn sub<CS: ConstraintSystem<ConstraintF>>(&self, _: CS, _: &Self) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn sub_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.sub(cs, other)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, _: CS) -> Result<Self, SynthesisError>;
|
||||
|
||||
#[inline]
|
||||
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.negate(cs)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn mul<CS: ConstraintSystem<ConstraintF>>(&self, _: CS, _: &Self) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn mul_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
other: &Self,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.mul(cs, other)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn square<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
self.mul(cs, &self)
|
||||
}
|
||||
|
||||
fn square_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.square(cs)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
result: &Self,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let actual_result = self.mul(cs.ns(|| "calc_actual_result"), other)?;
|
||||
result.enforce_equal(&mut cs.ns(|| "test_equals"), &actual_result)
|
||||
}
|
||||
|
||||
fn square_equals<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
result: &Self,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let actual_result = self.square(cs.ns(|| "calc_actual_result"))?;
|
||||
result.enforce_equal(&mut cs.ns(|| "test_equals"), &actual_result)
|
||||
}
|
||||
|
||||
fn add_constant<CS: ConstraintSystem<ConstraintF>>(&self, _: CS, _: &F) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
other: &F,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.add_constant(cs, other)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn sub_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
cs: CS,
|
||||
fe: &F,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
self.add_constant(cs, &(-(*fe)))
|
||||
}
|
||||
|
||||
fn sub_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
other: &F,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
self.add_constant_in_place(cs, &(-(*other)))
|
||||
}
|
||||
|
||||
fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
_: CS,
|
||||
_: &F,
|
||||
) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn mul_by_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
other: &F,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.mul_by_constant(cs, other)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn inverse<CS: ConstraintSystem<ConstraintF>>(&self, _: CS) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
_: CS,
|
||||
power: usize,
|
||||
) -> Result<Self, SynthesisError>;
|
||||
|
||||
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
&mut self,
|
||||
cs: CS,
|
||||
power: usize,
|
||||
) -> Result<&mut Self, SynthesisError> {
|
||||
*self = self.frobenius_map(cs, power)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Accepts as input a list of bits which, when interpreted in big-endian
|
||||
/// form, are a scalar.
|
||||
#[inline]
|
||||
fn pow<CS: ConstraintSystem<ConstraintF>>(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
bits: &[Boolean],
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let mut res = Self::one(cs.ns(|| "Alloc result"))?;
|
||||
for (i, bit) in bits.into_iter().enumerate() {
|
||||
res = res.square(cs.ns(|| format!("Double {}", i)))?;
|
||||
let tmp = res.mul(cs.ns(|| format!("Add {}-th base power", i)), self)?;
|
||||
res = Self::conditionally_select(
|
||||
cs.ns(|| format!("Conditional Select {}", i)),
|
||||
bit,
|
||||
&tmp,
|
||||
&res,
|
||||
)?;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn cost_of_mul() -> usize;
|
||||
|
||||
fn cost_of_inv() -> usize;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::{self, thread_rng, Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use super::FieldGadget;
|
||||
use crate::{
|
||||
bits::boolean::Boolean, test_constraint_system::TestConstraintSystem, utils::AllocGadget,
|
||||
};
|
||||
use algebra::{fields::Field, BitIterator};
|
||||
use r1cs_core::ConstraintSystem;
|
||||
|
||||
fn field_test<FE: Field, ConstraintF: Field, F: FieldGadget<FE, ConstraintF>, CS: ConstraintSystem<ConstraintF>>(
|
||||
mut cs: CS,
|
||||
a: F,
|
||||
b: F,
|
||||
) {
|
||||
let a_native = a.get_value().unwrap();
|
||||
let b_native = b.get_value().unwrap();
|
||||
|
||||
let zero = F::zero(cs.ns(|| "zero")).unwrap();
|
||||
let zero_native = zero.get_value().unwrap();
|
||||
zero.enforce_equal(&mut cs.ns(|| "zero_equals?"), &zero)
|
||||
.unwrap();
|
||||
assert_eq!(zero, zero);
|
||||
|
||||
let one = F::one(cs.ns(|| "one")).unwrap();
|
||||
let one_native = one.get_value().unwrap();
|
||||
assert_eq!(one, one);
|
||||
one.enforce_equal(&mut cs.ns(|| "one_equals?"), &one)
|
||||
.unwrap();
|
||||
assert_ne!(one, zero);
|
||||
|
||||
let one_dup = zero.add(cs.ns(|| "zero_plus_one"), &one).unwrap();
|
||||
one_dup
|
||||
.enforce_equal(&mut cs.ns(|| "one_plus_zero_equals"), &one)
|
||||
.unwrap();
|
||||
assert_eq!(one_dup, one);
|
||||
|
||||
let two = one.add(cs.ns(|| "one_plus_one"), &one).unwrap();
|
||||
two.enforce_equal(&mut cs.ns(|| "two_equals?"), &two)
|
||||
.unwrap();
|
||||
assert_eq!(two, two);
|
||||
assert_ne!(zero, two);
|
||||
assert_ne!(one, two);
|
||||
|
||||
// a == a
|
||||
assert_eq!(a, a);
|
||||
|
||||
// a + 0 = a
|
||||
let a_plus_zero = a.add(cs.ns(|| "a_plus_zero"), &zero).unwrap();
|
||||
assert_eq!(a_plus_zero, a);
|
||||
assert_eq!(a_plus_zero.get_value().unwrap(), a_native);
|
||||
a_plus_zero
|
||||
.enforce_equal(&mut cs.ns(|| "a_plus_zero_equals?"), &a)
|
||||
.unwrap();
|
||||
|
||||
// a - 0 = a
|
||||
let a_minus_zero = a.sub(cs.ns(|| "a_minus_zero"), &zero).unwrap();
|
||||
assert_eq!(a_minus_zero, a);
|
||||
assert_eq!(a_minus_zero.get_value().unwrap(), a_native);
|
||||
a_minus_zero
|
||||
.enforce_equal(&mut cs.ns(|| "a_minus_zero_equals?"), &a)
|
||||
.unwrap();
|
||||
|
||||
// a - a = 0
|
||||
let a_minus_a = a.sub(cs.ns(|| "a_minus_a"), &a).unwrap();
|
||||
assert_eq!(a_minus_a, zero);
|
||||
assert_eq!(a_minus_a.get_value().unwrap(), zero_native);
|
||||
a_minus_a
|
||||
.enforce_equal(&mut cs.ns(|| "a_minus_a_equals?"), &zero)
|
||||
.unwrap();
|
||||
|
||||
// a + b = b + a
|
||||
let a_b = a.add(cs.ns(|| "a_plus_b"), &b).unwrap();
|
||||
let b_a = b.add(cs.ns(|| "b_plus_a"), &a).unwrap();
|
||||
assert_eq!(a_b, b_a);
|
||||
assert_eq!(a_b.get_value().unwrap(), a_native + &b_native);
|
||||
a_b.enforce_equal(&mut cs.ns(|| "a+b == b+a"), &b_a)
|
||||
.unwrap();
|
||||
|
||||
// (a + b) + a = a + (b + a)
|
||||
let ab_a = a_b.add(cs.ns(|| "a_b_plus_a"), &a).unwrap();
|
||||
let a_ba = a.add(cs.ns(|| "a_plus_b_a"), &b_a).unwrap();
|
||||
assert_eq!(ab_a, a_ba);
|
||||
assert_eq!(ab_a.get_value().unwrap(), a_native + &b_native + &a_native);
|
||||
ab_a.enforce_equal(&mut cs.ns(|| "a+b + a == a+ b+a"), &a_ba)
|
||||
.unwrap();
|
||||
|
||||
let b_times_a_plus_b = a_b.mul(cs.ns(|| "b * (a + b)"), &b).unwrap();
|
||||
let b_times_b_plus_a = b_a.mul(cs.ns(|| "b * (b + a)"), &b).unwrap();
|
||||
assert_eq!(b_times_b_plus_a, b_times_a_plus_b);
|
||||
assert_eq!(
|
||||
b_times_a_plus_b.get_value().unwrap(),
|
||||
b_native * &(b_native + &a_native)
|
||||
);
|
||||
assert_eq!(
|
||||
b_times_a_plus_b.get_value().unwrap(),
|
||||
(b_native + &a_native) * &b_native
|
||||
);
|
||||
assert_eq!(
|
||||
b_times_a_plus_b.get_value().unwrap(),
|
||||
(a_native + &b_native) * &b_native
|
||||
);
|
||||
b_times_b_plus_a
|
||||
.enforce_equal(&mut cs.ns(|| "b*(a+b) == b * (b+a)"), &b_times_a_plus_b)
|
||||
.unwrap();
|
||||
|
||||
// a * 0 = 0
|
||||
assert_eq!(a.mul(cs.ns(|| "a_times_zero"), &zero).unwrap(), zero);
|
||||
|
||||
// a * 1 = a
|
||||
assert_eq!(a.mul(cs.ns(|| "a_times_one"), &one).unwrap(), a);
|
||||
assert_eq!(
|
||||
a.mul(cs.ns(|| "a_times_one2"), &one)
|
||||
.unwrap()
|
||||
.get_value()
|
||||
.unwrap(),
|
||||
a_native * &one_native
|
||||
);
|
||||
|
||||
// a * b = b * a
|
||||
let ab = a.mul(cs.ns(|| "a_times_b"), &b).unwrap();
|
||||
let ba = b.mul(cs.ns(|| "b_times_a"), &a).unwrap();
|
||||
assert_eq!(ab, ba);
|
||||
assert_eq!(ab.get_value().unwrap(), a_native * &b_native);
|
||||
|
||||
// (a * b) * a = a * (b * a)
|
||||
let ab_a = ab.mul(cs.ns(|| "ab_times_a"), &a).unwrap();
|
||||
let a_ba = a.mul(cs.ns(|| "a_times_ba"), &ba).unwrap();
|
||||
assert_eq!(ab_a, a_ba);
|
||||
assert_eq!(ab_a.get_value().unwrap(), a_native * &b_native * &a_native);
|
||||
|
||||
let aa = a.mul(cs.ns(|| "a * a"), &a).unwrap();
|
||||
let a_squared = a.square(cs.ns(|| "a^2")).unwrap();
|
||||
a_squared
|
||||
.enforce_equal(&mut cs.ns(|| "a^2 == a*a"), &aa)
|
||||
.unwrap();
|
||||
assert_eq!(aa, a_squared);
|
||||
assert_eq!(aa.get_value().unwrap(), a_native.square());
|
||||
|
||||
let aa = a
|
||||
.mul_by_constant(cs.ns(|| "a * a via mul_by_const"), &a.get_value().unwrap())
|
||||
.unwrap();
|
||||
a_squared
|
||||
.enforce_equal(&mut cs.ns(|| "a^2 == a*a via mul_by_const"), &aa)
|
||||
.unwrap();
|
||||
assert_eq!(aa, a_squared);
|
||||
assert_eq!(aa.get_value().unwrap(), a_native.square());
|
||||
|
||||
let a_b2 = a
|
||||
.add_constant(cs.ns(|| "a + b via add_const"), &b.get_value().unwrap())
|
||||
.unwrap();
|
||||
a_b.enforce_equal(&mut cs.ns(|| "a + b == a + b via add_const"), &a_b2)
|
||||
.unwrap();
|
||||
assert_eq!(a_b, a_b2);
|
||||
|
||||
let a_inv = a.inverse(cs.ns(|| "a_inv")).unwrap();
|
||||
a_inv
|
||||
.mul_equals(cs.ns(|| "check_equals"), &a, &one)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
a_inv.get_value().unwrap(),
|
||||
a.get_value().unwrap().inverse().unwrap()
|
||||
);
|
||||
assert_eq!(a_inv.get_value().unwrap(), a_native.inverse().unwrap());
|
||||
// a * a * a = a^3
|
||||
let bits = BitIterator::new([0x3])
|
||||
.map(|bit| Boolean::constant(bit))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
a_native * &(a_native * &a_native),
|
||||
a.pow(cs.ns(|| "test_pow"), &bits)
|
||||
.unwrap()
|
||||
.get_value()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
// a * a * a = a^3
|
||||
let mut constants = [FE::zero(); 4];
|
||||
for c in &mut constants {
|
||||
*c = rand::random();
|
||||
println!("Current c[i]: {:?}", c);
|
||||
}
|
||||
let bits = [Boolean::constant(false), Boolean::constant(true)];
|
||||
let lookup_result =
|
||||
F::two_bit_lookup(cs.ns(|| "Lookup"), &bits, constants.as_ref()).unwrap();
|
||||
assert_eq!(lookup_result.get_value().unwrap(), constants[2]);
|
||||
|
||||
let negone: FE = rand::random();
|
||||
|
||||
let n = F::alloc(&mut cs.ns(|| "alloc new var"), || Ok(negone)).unwrap();
|
||||
let _ = n.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap();
|
||||
let _ = n.to_bytes_strict(&mut cs.ns(|| "ToBytes Strict")).unwrap();
|
||||
}
|
||||
|
||||
fn random_frobenius_tests<
|
||||
FE: Field,
|
||||
ConstraintF: Field,
|
||||
F: FieldGadget<FE, ConstraintF>,
|
||||
CS: ConstraintSystem<ConstraintF>,
|
||||
>(
|
||||
mut cs: CS,
|
||||
maxpower: usize,
|
||||
) {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
for i in 0..(maxpower + 1) {
|
||||
let mut a = FE::rand(&mut rng);
|
||||
let mut a_gadget = F::alloc(cs.ns(|| format!("a_gadget_{:?}", i)), || Ok(a)).unwrap();
|
||||
a_gadget = a_gadget
|
||||
.frobenius_map(cs.ns(|| format!("frob_map_{}", i)), i)
|
||||
.unwrap();
|
||||
a.frobenius_map(i);
|
||||
|
||||
assert_eq!(a_gadget.get_value().unwrap(), a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bls12_377_field_gadgets_test() {
|
||||
use crate::fields::bls12_377::{Fq12Gadget, Fq2Gadget, Fq6Gadget, FqGadget};
|
||||
use algebra::fields::bls12_377::{Fq, Fq12, Fq2, Fq6};
|
||||
|
||||
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
|
||||
|
||||
let a = FqGadget::alloc(&mut cs.ns(|| "generate_a"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
let b = FqGadget::alloc(&mut cs.ns(|| "generate_b"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
field_test(cs.ns(|| "test_fq"), a, b);
|
||||
if !cs.is_satisfied() {
|
||||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
}
|
||||
|
||||
let c = Fq2Gadget::alloc(&mut cs.ns(|| "generate_c"), || Ok(Fq2::rand(&mut rng))).unwrap();
|
||||
let d = Fq2Gadget::alloc(&mut cs.ns(|| "generate_d"), || Ok(Fq2::rand(&mut rng))).unwrap();
|
||||
field_test(cs.ns(|| "test_fq2"), c, d);
|
||||
random_frobenius_tests::<Fq2, _, Fq2Gadget, _>(cs.ns(|| "test_frob_fq2"), 13);
|
||||
if !cs.is_satisfied() {
|
||||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
}
|
||||
|
||||
let a = Fq6Gadget::alloc(&mut cs.ns(|| "generate_e"), || Ok(Fq6::rand(&mut rng))).unwrap();
|
||||
let b = Fq6Gadget::alloc(&mut cs.ns(|| "generate_f"), || Ok(Fq6::rand(&mut rng))).unwrap();
|
||||
field_test(cs.ns(|| "test_fq6"), a, b);
|
||||
random_frobenius_tests::<Fq6, _, Fq6Gadget, _>(cs.ns(|| "test_frob_fq6"), 13);
|
||||
if !cs.is_satisfied() {
|
||||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
}
|
||||
|
||||
let c =
|
||||
Fq12Gadget::alloc(&mut cs.ns(|| "generate_g"), || Ok(Fq12::rand(&mut rng))).unwrap();
|
||||
let d =
|
||||
Fq12Gadget::alloc(&mut cs.ns(|| "generate_h"), || Ok(Fq12::rand(&mut rng))).unwrap();
|
||||
field_test(cs.ns(|| "test_fq12"), c, d);
|
||||
random_frobenius_tests::<Fq12, _, Fq12Gadget, _>(cs.ns(|| "test_frob_fq12"), 13);
|
||||
if !cs.is_satisfied() {
|
||||
println!("Here!");
|
||||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
}
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jubjub_field_gadgets_test() {
|
||||
use crate::fields::jubjub::FqGadget;
|
||||
use algebra::fields::jubjub::fq::Fq;
|
||||
|
||||
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let a = FqGadget::alloc(&mut cs.ns(|| "generate_a"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
let b = FqGadget::alloc(&mut cs.ns(|| "generate_b"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
field_test(cs.ns(|| "test_fq"), a, b);
|
||||
if !cs.is_satisfied() {
|
||||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
}
|
||||
assert!(cs.is_satisfied());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edwards_field_gadgets_test() {
|
||||
use crate::fields::edwards_bls12::FqGadget;
|
||||
use algebra::fields::edwards_bls12::fq::Fq;
|
||||
|
||||
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let a = FqGadget::alloc(&mut cs.ns(|| "generate_a"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
let b = FqGadget::alloc(&mut cs.ns(|| "generate_b"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
field_test(cs.ns(|| "test_fq"), a, b);
|
||||
if !cs.is_satisfied() {
|
||||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
}
|
||||
assert!(cs.is_satisfied());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user