Browse Source

Fix #172 and improve squaring code for deg-3 extensions (#174)

master
Pratyush Mishra 4 years ago
committed by GitHub
parent
commit
456f30a849
10 changed files with 255 additions and 443 deletions
  1. +25
    -25
      r1cs-std/src/bits/uint64.rs
  2. +3
    -48
      r1cs-std/src/fields/fp12.rs
  3. +3
    -48
      r1cs-std/src/fields/fp2.rs
  4. +100
    -109
      r1cs-std/src/fields/fp3.rs
  5. +3
    -50
      r1cs-std/src/fields/fp4.rs
  6. +3
    -50
      r1cs-std/src/fields/fp6_2over3.rs
  7. +98
    -105
      r1cs-std/src/fields/fp6_3over2.rs
  8. +15
    -3
      r1cs-std/src/fields/mod.rs
  9. +1
    -1
      r1cs-std/src/lib.rs
  10. +4
    -4
      r1cs-std/src/test_constraint_counter.rs

+ 25
- 25
r1cs-std/src/bits/uint64.rs

@ -14,7 +14,7 @@ use core::borrow::Borrow;
#[derive(Clone, Debug)]
pub struct UInt64 {
// Least significant bit_gadget first
bits: Vec<Boolean>,
bits: Vec<Boolean>,
value: Option<u64>,
}
@ -56,7 +56,7 @@ impl UInt64 {
}
v
}
},
None => vec![None; 64],
};
@ -95,19 +95,19 @@ impl UInt64 {
if b {
value.as_mut().map(|v| *v |= 1);
}
}
},
&Boolean::Is(ref b) => match b.get_value() {
Some(true) => {
value.as_mut().map(|v| *v |= 1);
}
Some(false) => {}
},
Some(false) => {},
None => value = None,
},
&Boolean::Not(ref b) => match b.get_value() {
Some(false) => {
value.as_mut().map(|v| *v |= 1);
}
Some(true) => {}
},
Some(true) => {},
None => value = None,
},
}
@ -129,7 +129,7 @@ impl UInt64 {
.collect();
UInt64 {
bits: new_bits,
bits: new_bits,
value: self.value.map(|v| v.rotate_right(by as u32)),
}
}
@ -194,12 +194,12 @@ impl UInt64 {
match op.value {
Some(val) => {
result_value.as_mut().map(|v| *v += u128::from(val));
}
},
None => {
// If any of our operands have unknown value, we won't
// know the value of the result
result_value = None;
}
},
}
// Iterate over each bit_gadget of the operand and add the operand to
@ -212,18 +212,18 @@ impl UInt64 {
// Add coeff * bit_gadget
lc += (coeff, bit.get_variable());
}
},
Boolean::Not(ref bit) => {
all_constants = false;
// Add coeff * (1 - bit_gadget) = coeff * ONE - coeff * bit_gadget
lc = lc + (coeff, CS::one()) - (coeff, bit.get_variable());
}
},
Boolean::Constant(bit) => {
if bit {
lc += (coeff, CS::one());
}
}
},
}
coeff.double_in_place();
@ -270,7 +270,7 @@ impl UInt64 {
result_bits.truncate(64);
Ok(UInt64 {
bits: result_bits,
bits: result_bits,
value: modular_value,
})
}
@ -330,7 +330,7 @@ impl ToBytesGadget for UInt64 {
let mut bytes = Vec::new();
for (i, chunk8) in self.to_bits_le().chunks(8).enumerate() {
let byte = UInt8 {
bits: chunk8.to_vec(),
bits: chunk8.to_vec(),
value: value_chunks[i],
};
bytes.push(byte);
@ -397,7 +397,7 @@ mod test {
match bit_gadget {
&Boolean::Constant(bit_gadget) => {
assert!(bit_gadget == ((b.value.unwrap() >> i) & 1 == 1));
}
},
_ => unreachable!(),
}
}
@ -406,8 +406,8 @@ mod test {
for x in v.iter().zip(expected_to_be_same.iter()) {
match x {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
(&Boolean::Constant(true), &Boolean::Constant(true)) => {},
(&Boolean::Constant(false), &Boolean::Constant(false)) => {},
_ => unreachable!(),
}
}
@ -442,13 +442,13 @@ mod test {
match b {
&Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
}
},
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}
},
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
},
}
expected >>= 1;
@ -483,7 +483,7 @@ mod test {
&Boolean::Not(_) => panic!(),
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
},
}
expected >>= 1;
@ -521,10 +521,10 @@ mod test {
match b {
&Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
}
},
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}
},
&Boolean::Constant(_) => unreachable!(),
}
@ -560,7 +560,7 @@ mod test {
match b {
&Boolean::Constant(b) => {
assert_eq!(b, tmp & 1 == 1);
}
},
_ => unreachable!(),
}

+ 3
- 48
r1cs-std/src/fields/fp12.rs

@ -6,11 +6,11 @@ use algebra::{
fp6_3over2::{Fp6, Fp6Parameters},
Fp2Parameters,
},
BitIterator, Field, One, PrimeField,
BitIterator, PrimeField,
};
use core::{borrow::Borrow, marker::PhantomData};
use crate::{prelude::*, Assignment, Vec};
use crate::{prelude::*, Vec};
type Fp2Gadget<P, ConstraintF> = super::fp2::Fp2Gadget<
<<P as Fp12Parameters>::Fp6Params as Fp6Parameters>::Fp2Params,
@ -546,47 +546,6 @@ where
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,
@ -639,16 +598,12 @@ where
}
fn cost_of_mul() -> usize {
unimplemented!()
3 * Fp6Gadget::<P, ConstraintF>::cost_of_mul()
}
fn cost_of_mul_equals() -> usize {
Self::cost_of_mul()
}
fn cost_of_inv() -> usize {
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
}
}
impl<P, ConstraintF: PrimeField> PartialEq for Fp12Gadget<P, ConstraintF>

+ 3
- 48
r1cs-std/src/fields/fp2.rs

@ -1,11 +1,11 @@
use algebra::{
fields::{Fp2, Fp2Parameters},
Field, PrimeField,
PrimeField,
};
use core::{borrow::Borrow, marker::PhantomData};
use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError};
use crate::{fields::fp::FpGadget, prelude::*, Assignment, Vec};
use crate::{fields::fp::FpGadget, prelude::*, Vec};
#[derive(Derivative)]
#[derivative(Debug(bound = "P: Fp2Parameters, ConstraintF: PrimeField"))]
@ -289,47 +289,6 @@ impl, ConstraintF: PrimeField> FieldGadget
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,
@ -450,16 +409,12 @@ impl, ConstraintF: PrimeField> FieldGadget
}
fn cost_of_mul() -> usize {
3
3 * FpGadget::<ConstraintF>::cost_of_mul()
}
fn cost_of_mul_equals() -> usize {
Self::cost_of_mul()
}
fn cost_of_inv() -> usize {
2
}
}
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> PartialEq

+ 100
- 109
r1cs-std/src/fields/fp3.rs

@ -1,14 +1,11 @@
use algebra::{
fields::{
fp3::{Fp3, Fp3Parameters},
Field,
},
fields::fp3::{Fp3, Fp3Parameters},
PrimeField, SquareRootField,
};
use core::{borrow::Borrow, marker::PhantomData};
use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError};
use crate::{fields::fp::FpGadget, prelude::*, Assignment, Vec};
use crate::{fields::fp::FpGadget, prelude::*, Vec};
#[derive(Derivative)]
#[derivative(Debug(
@ -40,6 +37,7 @@ impl, ConstraintF: PrimeField + SquareRootFie
_params: PhantomData,
}
}
/// Multiply a FpGadget by quadratic nonresidue P::NONRESIDUE.
#[inline]
pub fn mul_fp_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
@ -332,120 +330,113 @@ impl, ConstraintF: PrimeField + SquareRootFie
Ok(Self::new(c0, c1, c2))
}
/// Use the Toom-Cook-3x method to compute multiplication.
#[inline]
fn square<CS: ConstraintSystem<ConstraintF>>(
fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Self, SynthesisError> {
// Uses Toom-Cook-3x multiplication from
other: &Self,
result: &Self,
) -> Result<(), SynthesisError> {
// Karatsuba multiplication for Fp3:
// v0 = A.c0 * B.c0
// v1 = A.c1 * B.c1
// v2 = A.c2 * B.c2
// result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2)
// result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2
// result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2,
// We enforce this with six constraints:
//
// v0 = A.c0 * B.c0
// v1 = A.c1 * B.c1
// v2 = A.c2 * B.c2
//
// result.c0 - v0 + \beta*(v1 + v2) = β(a1 + a2)(b1 + b2))
// result.c1 + v0 + v1 - βv2 = (a0 + a1)(b0 + b1)
// result.c2 + v0 - v1 + v2 = (a0 + a2)(b0 + b2)
// Reference:
// "Multiplication and Squaring on Pairing-Friendly Fields"
// Devegili, OhEigeartaigh, Scott, Dahab
// v0 = a(0)^2 = a0^2
let v0 = self.c0.square(&mut cs.ns(|| "Calc v0"))?;
// v1 = a(1)^2 = (a0 + a1 + a2)^2
let v1 = {
let a0_plus_a1_plus_a2 = self
.c0
.add(cs.ns(|| "a0 + a1"), &self.c1)?
.add(cs.ns(|| "a0 + a1 + a2"), &self.c2)?;
a0_plus_a1_plus_a2.square(&mut cs.ns(|| "(a0 + a1 + a2)^2"))?
};
// v2 = a(−1)^2 = (a0 − a1 + a2)^2
let v2 = {
let a0_minus_a1_plus_a2 = self
.c0
.sub(cs.ns(|| "a0 - a1"), &self.c1)?
.add(cs.ns(|| "a0 - a2 + a2"), &self.c2)?;
a0_minus_a1_plus_a2.square(&mut cs.ns(|| "(a0 - a1 + a2)^2"))?
};
// v3 = a(2)^2 = (a0 + 2a1 + 4a2)^2
let v3 = {
let a1_double = self.c1.double(cs.ns(|| "2a1"))?;
let a2_quad = self.c2.double(cs.ns(|| "2a2"))?.double(cs.ns(|| "4a2"))?;
let a0_plus_2_a1_plus_4_a2 = self
.c0
.add(cs.ns(|| "a0 + 2a1"), &a1_double)?
.add(cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?;
a0_plus_2_a1_plus_4_a2.square(&mut cs.ns(|| "(a0 + 2a1 + 4a2)^2"))?
};
// v4 = a(∞)^2 = a2^2
let v4 = self.c2.square(&mut cs.ns(|| "a2^2"))?;
let two = P::Fp::one().double();
let six = two.double() + &two;
let mut two_and_six = [two, six];
algebra::fields::batch_inversion(&mut two_and_six);
let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
let half_v0 = v0.mul_by_constant(cs.ns(|| "half_v0"), &two_inverse)?;
let half_v1 = v1.mul_by_constant(cs.ns(|| "half_v1"), &two_inverse)?;
let one_sixth_v2 = v2.mul_by_constant(cs.ns(|| "one_sixth_v2"), &six_inverse)?;
let one_sixth_v3 = v3.mul_by_constant(cs.ns(|| "one_sixth_v3"), &six_inverse)?;
let two_v4 = v4.double(cs.ns(|| "double_v4"))?;
// c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4)
let c0 = {
let mut c0_cs = cs.ns(|| "c0");
// No constraints, only get a linear combination back.
let inner = half_v0
.sub(c0_cs.ns(|| "sub1"), &half_v1)?
.sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)?
.add(c0_cs.ns(|| "add3"), &one_sixth_v3)?
.sub(c0_cs.ns(|| "sub4"), &two_v4)?;
let non_residue_times_inner =
inner.mul_by_constant(c0_cs.ns(|| "mul_by_res"), &P::NONRESIDUE)?;
v0.add(c0_cs.ns(|| "add5"), &non_residue_times_inner)?
};
// −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4
let c1 = {
let mut c1_cs = cs.ns(|| "c1");
let one_third_v2 = one_sixth_v2.double(c1_cs.ns(|| "v2_by_3"))?;
let non_residue_v4 = v4.mul_by_constant(c1_cs.ns(|| "mul_by_res"), &P::NONRESIDUE)?;
half_v0
.negate(c1_cs.ns(|| "neg1"))?
.add(c1_cs.ns(|| "add1"), &v1)?
.sub(c1_cs.ns(|| "sub2"), &one_third_v2)?
.sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)?
.add(c1_cs.ns(|| "add4"), &two_v4)?
.add(c1_cs.ns(|| "add5"), &non_residue_v4)?
};
// -v0 + (1/2)v1 + (1/2)v2 −v4
let c2 = {
let mut c2_cs = cs.ns(|| "c2");
let half_v2 = v2.mul_by_constant(c2_cs.ns(|| "half_v2"), &two_inverse)?;
half_v1
.add(c2_cs.ns(|| "add1"), &half_v2)?
.sub(c2_cs.ns(|| "sub1"), &v4)?
.sub(c2_cs.ns(|| "sub2"), &v0)?
};
Ok(Self::new(c0, c1, c2))
// Devegili, OhEigeartaigh, Scott, Dahab
//
// This implementation adapted from
// https://github.com/ZencashOfficial/ginger-lib/blob/development/r1cs/gadgets/std/src/fields/fp3.rs
let v0 = self.c0.mul(cs.ns(|| "v0 = a0 * b0"), &other.c0)?;
let v1 = self.c1.mul(cs.ns(|| "v1 = a1 * b1"), &other.c1)?;
let v2 = self.c2.mul(cs.ns(|| "v2 = a2 * b2"), &other.c2)?;
// Check c0
let nr_a1_plus_a2 = self
.c1
.add(cs.ns(|| "a1 + a2"), &self.c2)?
.mul_by_constant(cs.ns(|| "nr*(a1 + a2)"), &P::NONRESIDUE)?;
let b1_plus_b2 = other.c1.add(cs.ns(|| "b1 + b2"), &other.c2)?;
let nr_v1 = v1.mul_by_constant(cs.ns(|| "nr * v1"), &P::NONRESIDUE)?;
let nr_v2 = v2.mul_by_constant(cs.ns(|| "nr * v2"), &P::NONRESIDUE)?;
let to_check = result
.c0
.sub(cs.ns(|| "c0 - v0"), &v0)?
.add(cs.ns(|| "c0 - v0 + nr * v1"), &nr_v1)?
.add(cs.ns(|| "c0 - v0 + nr * v1 + nr * v2"), &nr_v2)?;
nr_a1_plus_a2.mul_equals(cs.ns(|| "check c0"), &b1_plus_b2, &to_check)?;
// Check c1
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?;
let to_check = result
.c1
.sub(cs.ns(|| "c1 - nr * v2"), &nr_v2)?
.add(cs.ns(|| "c1 - nr * v2 + v0"), &v0)?
.add(cs.ns(|| "c1 - nr * v2 + v0 + v1"), &v1)?;
a0_plus_a1.mul_equals(cs.ns(|| "check c1"), &b0_plus_b1, &to_check)?;
// Check c2
let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?;
let b0_plus_b2 = other.c0.add(cs.ns(|| "b0 + b2"), &other.c2)?;
let to_check = result
.c2
.add(cs.ns(|| "c2 + v0"), &v0)?
.sub(cs.ns(|| "c2 + v0 - v1"), &v1)?
.add(cs.ns(|| "c2 + v0 - v1 + v2"), &v2)?;
a0_plus_a2.mul_equals(cs.ns(|| "check c2"), &b0_plus_b2, &to_check)?;
Ok(())
}
// 18 constraints, we can probably do better but not sure it's worth it.
/// Use the Chung-Hasan asymmetric squaring formula.
///
/// (Devegili OhEig Scott Dahab --- Multiplication and Squaring on
/// Abstract Pairing-Friendly
/// Fields.pdf; Section 4 (CH-SQR2))
#[inline]
fn inverse<CS: ConstraintSystem<ConstraintF>>(
fn square<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)
let a = self.c0.clone();
let b = self.c1.clone();
let c = self.c2.clone();
let s0 = a.square(cs.ns(|| "s0"))?;
let ab = a.mul(cs.ns(|| "ab"), &b)?;
let s1 = ab.double(cs.ns(|| "s1"))?;
let s2 = a
.sub(cs.ns(|| "a-b"), &b)?
.add(cs.ns(|| "plus c"), &c)?
.square(cs.ns(|| "s2"))?;
let s3 = b.mul(cs.ns(|| "bc"), &c)?.double(cs.ns(|| "s3"))?;
let s4 = c.square(cs.ns(|| "s4"))?;
let c0 = Self::mul_fp_gadget_by_nonresidue(cs.ns(|| "c0 part 1"), &s3)?
.add(cs.ns(|| "c0"), &s0)?;
let c1 = Self::mul_fp_gadget_by_nonresidue(cs.ns(|| "c1 part 1"), &s4)?
.add(cs.ns(|| "c1"), &s1)?;
let c2 = s1
.add(cs.ns(|| "c2 part1"), &s2)?
.add(cs.ns(|| "c2 part2"), &s3)?
.sub(cs.ns(|| "c2 part3"), &s0)?
.sub(cs.ns(|| "c2 part4"), &s4)?;
Ok(Self::new(c0, c1, c2))
}
#[inline]
@ -634,8 +625,8 @@ impl, ConstraintF: PrimeField + SquareRootFie
5 * FpGadget::<ConstraintF>::cost_of_mul()
}
fn cost_of_inv() -> usize {
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
fn cost_of_mul_equals() -> usize {
6 * FpGadget::<ConstraintF>::cost_of_mul()
}
}

+ 3
- 50
r1cs-std/src/fields/fp4.rs

@ -1,11 +1,11 @@
use algebra::{
fields::{Fp2, Fp2Parameters, Fp4, Fp4Parameters},
BigInteger, Field, One, PrimeField,
BigInteger, PrimeField,
};
use core::{borrow::Borrow, marker::PhantomData};
use r1cs_core::{ConstraintSystem, SynthesisError};
use crate::{prelude::*, Assignment, Vec};
use crate::{prelude::*, Vec};
type Fp2Gadget<P, ConstraintF> =
super::fp2::Fp2Gadget<<P as Fp4Parameters>::Fp2Params, ConstraintF>;
@ -308,48 +308,6 @@ where
Ok(Self::new(c0, c1))
}
#[inline]
fn inverse<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Self, SynthesisError> {
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
self.get_value().and_then(|val| val.inverse()).get()
})?;
// Karatsuba multiplication for Fp4 with the inverse:
// v0 = A.c0 * B.c0
// v1 = A.c1 * B.c1
//
// 1 = v0 + non_residue * v1
// => v0 = 1 - non_residue * v1
//
// 0 = result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
// => v0 + v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
// Enforced with 2 constraints:
// A.c1 * B.c1 = v1
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
// Reference:
// "Multiplication and Squaring on Pairing-Friendly Fields"
// Devegili, OhEigeartaigh, Scott, Dahab
// Constraint 1
let v1 = self.c1.mul(cs.ns(|| "inv_constraint_1"), &inverse.c1)?;
// Constraint 2
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
let b0_plus_b1 = inverse.c0.add(cs.ns(|| "b0 + b1"), &inverse.c1)?;
let one = Fp2::<<P as Fp4Parameters>::Fp2Params>::one();
let rhs = Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "nr * v1"), &v1)?
.sub(cs.ns(|| "sub v1"), &v1)?
.negate(cs.ns(|| "negate it"))?
.add_constant(cs.ns(|| "add one"), &one)?;
a0_plus_a1.mul_equals(cs.ns(|| "inv_constraint_2"), &b0_plus_b1, &rhs)?;
Ok(inverse)
}
fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
@ -475,17 +433,12 @@ where
}
fn cost_of_mul() -> usize {
3 * <Fp2Gadget<P, ConstraintF> as FieldGadget<Fp2<P::Fp2Params>, ConstraintF>>::cost_of_mul(
)
3 * Fp2Gadget::<P, ConstraintF>::cost_of_mul()
}
fn cost_of_mul_equals() -> usize {
Self::cost_of_mul()
}
fn cost_of_inv() -> usize {
unimplemented!()
}
}
impl<P, ConstraintF: PrimeField> PartialEq for Fp4Gadget<P, ConstraintF>

+ 3
- 50
r1cs-std/src/fields/fp6_2over3.rs

@ -3,12 +3,12 @@ use algebra::{
fp6_2over3::{Fp6, Fp6Parameters},
Fp3, Fp3Parameters,
},
BigInteger, Field, One, PrimeField, SquareRootField,
BigInteger, PrimeField, SquareRootField,
};
use core::{borrow::Borrow, marker::PhantomData};
use r1cs_core::{ConstraintSystem, SynthesisError};
use crate::{prelude::*, Assignment, Vec};
use crate::{prelude::*, Vec};
type Fp3Gadget<P, ConstraintF> =
super::fp3::Fp3Gadget<<P as Fp6Parameters>::Fp3Params, ConstraintF>;
@ -291,48 +291,6 @@ where
Ok(Self::new(c0, c1))
}
#[inline]
fn inverse<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Self, SynthesisError> {
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
self.get_value().and_then(|val| val.inverse()).get()
})?;
// Karatsuba multiplication for Fp6 with the inverse:
// v0 = A.c0 * B.c0
// v1 = A.c1 * B.c1
//
// 1 = v0 + non_residue * v1
// => v0 = 1 - non_residue * v1
//
// 0 = result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
// => v0 + v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
// Enforced with 2 constraints:
// A.c1 * B.c1 = v1
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
// Reference:
// "Multiplication and Squaring on Pairing-Friendly Fields"
// Devegili, OhEigeartaigh, Scott, Dahab
// Constraint 1
let v1 = self.c1.mul(cs.ns(|| "inv_constraint_1"), &inverse.c1)?;
// Constraint 2
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
let b0_plus_b1 = inverse.c0.add(cs.ns(|| "b0 + b1"), &inverse.c1)?;
let one = Fp3::<<P as Fp6Parameters>::Fp3Params>::one();
let rhs = Self::mul_fp3_gadget_by_nonresidue(cs.ns(|| "nr * v1"), &v1)?
.sub(cs.ns(|| "sub v1"), &v1)?
.negate(cs.ns(|| "negate it"))?
.add_constant(cs.ns(|| "add one"), &one)?;
a0_plus_a1.mul_equals(cs.ns(|| "inv_constraint_2"), &b0_plus_b1, &rhs)?;
Ok(inverse)
}
fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
@ -458,17 +416,12 @@ where
}
fn cost_of_mul() -> usize {
3 * <Fp3Gadget<P, ConstraintF> as FieldGadget<Fp3<P::Fp3Params>, ConstraintF>>::cost_of_mul(
)
2 * Fp3Gadget::<P, ConstraintF>::cost_of_mul()
}
fn cost_of_mul_equals() -> usize {
Self::cost_of_mul()
}
fn cost_of_inv() -> usize {
unimplemented!()
}
}
impl<P, ConstraintF: PrimeField + SquareRootField> PartialEq for Fp6Gadget<P, ConstraintF>

+ 98
- 105
r1cs-std/src/fields/fp6_3over2.rs

@ -8,7 +8,7 @@ use algebra::{
use core::{borrow::Borrow, marker::PhantomData};
use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError};
use crate::{prelude::*, Assignment, Vec};
use crate::{prelude::*, Vec};
type Fp2Gadget<P, ConstraintF> =
super::fp2::Fp2Gadget<<P as Fp6Parameters>::Fp2Params, ConstraintF>;
@ -398,120 +398,113 @@ where
Ok(Self::new(c0, c1, c2))
}
/// Use the Toom-Cook-3x method to compute multiplication.
/// Use the Chung-Hasan asymmetric squaring formula.
///
/// (Devegili OhEig Scott Dahab --- Multiplication and Squaring on
/// Abstract Pairing-Friendly
/// Fields.pdf; Section 4 (CH-SQR2))
#[inline]
fn square<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Self, SynthesisError> {
// Uses Toom-Cook-3x multiplication from
//
// Reference:
// "Multiplication and Squaring on Pairing-Friendly Fields"
// Devegili, OhEigeartaigh, Scott, Dahab
// v0 = a(0)^2 = a0^2
let v0 = self.c0.square(&mut cs.ns(|| "Calc v0"))?;
// v1 = a(1)^2 = (a0 + a1 + a2)^2
let v1 = {
let a0_plus_a1_plus_a2 = self
.c0
.add(cs.ns(|| "a0 + a1"), &self.c1)?
.add(cs.ns(|| "a0 + a1 + a2"), &self.c2)?;
a0_plus_a1_plus_a2.square(&mut cs.ns(|| "(a0 + a1 + a2)^2"))?
};
// v2 = a(−1)^2 = (a0 − a1 + a2)^2
let v2 = {
let a0_minus_a1_plus_a2 = self
.c0
.sub(cs.ns(|| "a0 - a1"), &self.c1)?
.add(cs.ns(|| "a0 - a2 + a2"), &self.c2)?;
a0_minus_a1_plus_a2.square(&mut cs.ns(|| "(a0 - a1 + a2)^2"))?
};
// v3 = a(2)^2 = (a0 + 2a1 + 4a2)^2
let v3 = {
let a1_double = self.c1.double(cs.ns(|| "2a1"))?;
let a2_quad = self.c2.double(cs.ns(|| "2a2"))?.double(cs.ns(|| "4a2"))?;
let a0_plus_2_a1_plus_4_a2 = self
.c0
.add(cs.ns(|| "a0 + 2a1"), &a1_double)?
.add(cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?;
a0_plus_2_a1_plus_4_a2.square(&mut cs.ns(|| "(a0 + 2a1 + 4a2)^2"))?
};
// v4 = a(∞)^2 = a2^2
let v4 = self.c2.square(&mut cs.ns(|| "a2^2"))?;
let two = <P::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)?
};
let a = self.c0.clone();
let b = self.c1.clone();
let c = self.c2.clone();
let s0 = a.square(cs.ns(|| "s0"))?;
let ab = a.mul(cs.ns(|| "ab"), &b)?;
let s1 = ab.double(cs.ns(|| "s1"))?;
let s2 = a
.sub(cs.ns(|| "a-b"), &b)?
.add(cs.ns(|| "plus c"), &c)?
.square(cs.ns(|| "s2"))?;
let s3 = b.mul(cs.ns(|| "bc"), &c)?.double(cs.ns(|| "s3"))?;
let s4 = c.square(cs.ns(|| "s4"))?;
let c0 = Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "c0 part 1"), &s3)?
.add(cs.ns(|| "c0"), &s0)?;
let c1 = Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "c1 part 1"), &s4)?
.add(cs.ns(|| "c1"), &s1)?;
let c2 = s1
.add(cs.ns(|| "c2 part1"), &s2)?
.add(cs.ns(|| "c2 part2"), &s3)?
.sub(cs.ns(|| "c2 part3"), &s0)?
.sub(cs.ns(|| "c2 part4"), &s4)?;
Ok(Self::new(c0, c1, c2))
}
// 18 constraints, we can probably do better but not sure it's worth it.
#[inline]
fn inverse<CS: ConstraintSystem<ConstraintF>>(
fn mul_equals<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)
other: &Self,
result: &Self,
) -> Result<(), SynthesisError> {
// Karatsuba multiplication for Fp3:
// v0 = A.c0 * B.c0
// v1 = A.c1 * B.c1
// v2 = A.c2 * B.c2
// result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2)
// result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2
// result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2,
// We enforce this with six constraints:
//
// v0 = A.c0 * B.c0
// v1 = A.c1 * B.c1
// v2 = A.c2 * B.c2
//
// result.c0 - v0 + \beta*(v1 + v2) = β(a1 + a2)(b1 + b2))
// result.c1 + v0 + v1 - βv2 = (a0 + a1)(b0 + b1)
// result.c2 + v0 - v1 + v2 = (a0 + a2)(b0 + b2)
// Reference:
// "Multiplication and Squaring on Pairing-Friendly Fields"
// Devegili, OhEigeartaigh, Scott, Dahab
//
// This implementation adapted from
// https://github.com/ZencashOfficial/ginger-lib/blob/development/r1cs/gadgets/std/src/fields/fp3.rs
let v0 = self.c0.mul(cs.ns(|| "v0 = a0 * b0"), &other.c0)?;
let v1 = self.c1.mul(cs.ns(|| "v1 = a1 * b1"), &other.c1)?;
let v2 = self.c2.mul(cs.ns(|| "v2 = a2 * b2"), &other.c2)?;
// Check c0
let nr_a1_plus_a2 = self
.c1
.add(cs.ns(|| "a1 + a2"), &self.c2)?
.mul_by_constant(cs.ns(|| "nr*(a1 + a2)"), &P::NONRESIDUE)?;
let b1_plus_b2 = other.c1.add(cs.ns(|| "b1 + b2"), &other.c2)?;
let nr_v1 = v1.mul_by_constant(cs.ns(|| "nr * v1"), &P::NONRESIDUE)?;
let nr_v2 = v2.mul_by_constant(cs.ns(|| "nr * v2"), &P::NONRESIDUE)?;
let to_check = result
.c0
.sub(cs.ns(|| "c0 - v0"), &v0)?
.add(cs.ns(|| "c0 - v0 + nr * v1"), &nr_v1)?
.add(cs.ns(|| "c0 - v0 + nr * v1 + nr * v2"), &nr_v2)?;
nr_a1_plus_a2.mul_equals(cs.ns(|| "check c0"), &b1_plus_b2, &to_check)?;
// Check c1
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?;
let to_check = result
.c1
.sub(cs.ns(|| "c1 - nr * v2"), &nr_v2)?
.add(cs.ns(|| "c1 - nr * v2 + v0"), &v0)?
.add(cs.ns(|| "c1 - nr * v2 + v0 + v1"), &v1)?;
a0_plus_a1.mul_equals(cs.ns(|| "check c1"), &b0_plus_b1, &to_check)?;
// Check c2
let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?;
let b0_plus_b2 = other.c0.add(cs.ns(|| "b0 + b2"), &other.c2)?;
let to_check = result
.c2
.add(cs.ns(|| "c2 + v0"), &v0)?
.sub(cs.ns(|| "c2 + v0 - v1"), &v1)?
.add(cs.ns(|| "c2 + v0 - v1 + v2"), &v2)?;
a0_plus_a2.mul_equals(cs.ns(|| "check c2"), &b0_plus_b2, &to_check)?;
Ok(())
}
#[inline]
@ -704,8 +697,8 @@ where
5 * Fp2Gadget::<P, ConstraintF>::cost_of_mul()
}
fn cost_of_inv() -> usize {
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
fn cost_of_mul_equals() -> usize {
6 * Fp2Gadget::<P, ConstraintF>::cost_of_mul()
}
}

+ 15
- 3
r1cs-std/src/fields/mod.rs

@ -2,7 +2,7 @@ use algebra::{fields::BitIterator, Field};
use core::fmt::Debug;
use r1cs_core::{ConstraintSystem, SynthesisError};
use crate::prelude::*;
use crate::{prelude::*, Assignment};
pub mod fp;
pub mod fp12;
@ -188,7 +188,17 @@ pub trait FieldGadget:
Ok(self)
}
fn inverse<CS: ConstraintSystem<ConstraintF>>(&self, _: CS) -> Result<Self, SynthesisError>;
fn inverse<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Self, SynthesisError> {
let one = Self::one(&mut cs.ns(|| "one"))?;
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
self.get_value().and_then(|val| val.inverse()).get()
})?;
self.mul_equals(cs.ns(|| "check inv"), &inverse, &one)?;
Ok(inverse)
}
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
&self,
@ -255,7 +265,9 @@ pub trait FieldGadget:
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
}
fn cost_of_inv() -> usize;
fn cost_of_inv() -> usize {
Self::cost_of_mul_equals()
}
}
#[cfg(test)]

+ 1
- 1
r1cs-std/src/lib.rs

@ -33,8 +33,8 @@ use ralloc::{collections::BTreeMap, string::String, vec::Vec};
#[cfg(feature = "std")]
use std::{collections::BTreeMap, string::String, vec::Vec};
pub mod test_constraint_system;
pub mod test_constraint_counter;
pub mod test_constraint_system;
pub mod bits;
pub use self::bits::*;

+ 4
- 4
r1cs-std/src/test_constraint_counter.rs

@ -4,16 +4,16 @@ use r1cs_core::{ConstraintSystem, Index, LinearCombination, SynthesisError, Vari
/// Constraint counter for testing purposes.
pub struct ConstraintCounter {
pub num_inputs: usize,
pub num_aux: usize,
pub num_inputs: usize,
pub num_aux: usize,
pub num_constraints: usize,
}
impl ConstraintCounter {
pub fn new() -> Self {
Self {
num_aux: 0,
num_inputs: 0,
num_aux: 0,
num_inputs: 0,
num_constraints: 0,
}
}

Loading…
Cancel
Save