From e00d667330ff359275d1b2b483616996a6fd61cf Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Mon, 12 Aug 2019 16:52:26 -0700 Subject: [PATCH] Move snark-gadgets to r1cs-std --- Cargo.toml | 2 +- r1cs-std/Cargo.toml | 31 + r1cs-std/LICENSE-APACHE | 1 + r1cs-std/LICENSE-MIT | 1 + r1cs-std/src/alloc.rs | 105 + r1cs-std/src/bits/boolean.rs | 1883 +++++++++++++++++ r1cs-std/src/bits/mod.rs | 84 + r1cs-std/src/bits/uint32.rs | 534 +++++ r1cs-std/src/bits/uint8.rs | 399 ++++ r1cs-std/src/eq.rs | 137 ++ r1cs-std/src/fields/bls12_377.rs | 10 + r1cs-std/src/fields/edwards_bls12.rs | 4 + r1cs-std/src/fields/edwards_sw6.rs | 4 + r1cs-std/src/fields/fp.rs | 571 +++++ r1cs-std/src/fields/fp12.rs | 854 ++++++++ r1cs-std/src/fields/fp2.rs | 627 ++++++ r1cs-std/src/fields/fp6_3over2.rs | 940 ++++++++ r1cs-std/src/fields/jubjub.rs | 6 + r1cs-std/src/fields/mod.rs | 512 +++++ r1cs-std/src/groups/curves/mod.rs | 2 + .../short_weierstrass/bls12/bls12_377.rs | 213 ++ .../curves/short_weierstrass/bls12/mod.rs | 168 ++ .../groups/curves/short_weierstrass/mod.rs | 625 ++++++ .../curves/twisted_edwards/edwards_bls12.rs | 32 + .../curves/twisted_edwards/edwards_sw6.rs | 32 + .../groups/curves/twisted_edwards/jubjub.rs | 32 + .../src/groups/curves/twisted_edwards/mod.rs | 1078 ++++++++++ .../src/groups/curves/twisted_edwards/test.rs | 77 + r1cs-std/src/groups/mod.rs | 218 ++ r1cs-std/src/lib.rs | 358 ++++ r1cs-std/src/pairing/bls12/bls12_377.rs | 4 + r1cs-std/src/pairing/bls12/bls12_381.rs | 0 r1cs-std/src/pairing/bls12/mod.rs | 206 ++ r1cs-std/src/pairing/mod.rs | 178 ++ r1cs-std/src/select.rs | 36 + r1cs-std/src/test_constraint_system.rs | 230 ++ 36 files changed, 10193 insertions(+), 1 deletion(-) create mode 100644 r1cs-std/Cargo.toml create mode 120000 r1cs-std/LICENSE-APACHE create mode 120000 r1cs-std/LICENSE-MIT create mode 100644 r1cs-std/src/alloc.rs create mode 100644 r1cs-std/src/bits/boolean.rs create mode 100644 r1cs-std/src/bits/mod.rs create mode 100644 r1cs-std/src/bits/uint32.rs create mode 100644 r1cs-std/src/bits/uint8.rs create mode 100644 r1cs-std/src/eq.rs create mode 100644 r1cs-std/src/fields/bls12_377.rs create mode 100644 r1cs-std/src/fields/edwards_bls12.rs create mode 100644 r1cs-std/src/fields/edwards_sw6.rs create mode 100644 r1cs-std/src/fields/fp.rs create mode 100644 r1cs-std/src/fields/fp12.rs create mode 100644 r1cs-std/src/fields/fp2.rs create mode 100644 r1cs-std/src/fields/fp6_3over2.rs create mode 100644 r1cs-std/src/fields/jubjub.rs create mode 100644 r1cs-std/src/fields/mod.rs create mode 100644 r1cs-std/src/groups/curves/mod.rs create mode 100644 r1cs-std/src/groups/curves/short_weierstrass/bls12/bls12_377.rs create mode 100644 r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs create mode 100644 r1cs-std/src/groups/curves/short_weierstrass/mod.rs create mode 100644 r1cs-std/src/groups/curves/twisted_edwards/edwards_bls12.rs create mode 100644 r1cs-std/src/groups/curves/twisted_edwards/edwards_sw6.rs create mode 100644 r1cs-std/src/groups/curves/twisted_edwards/jubjub.rs create mode 100644 r1cs-std/src/groups/curves/twisted_edwards/mod.rs create mode 100644 r1cs-std/src/groups/curves/twisted_edwards/test.rs create mode 100644 r1cs-std/src/groups/mod.rs create mode 100644 r1cs-std/src/lib.rs create mode 100644 r1cs-std/src/pairing/bls12/bls12_377.rs create mode 100644 r1cs-std/src/pairing/bls12/bls12_381.rs create mode 100644 r1cs-std/src/pairing/bls12/mod.rs create mode 100644 r1cs-std/src/pairing/mod.rs create mode 100644 r1cs-std/src/select.rs create mode 100644 r1cs-std/src/test_constraint_system.rs diff --git a/Cargo.toml b/Cargo.toml index 1c3c5dc..08983aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] -members = [ "dpc", "algebra", "r1cs-core", "r1cs-lib", "gm17", "snark-gadgets", "bench-utils" ] +members = [ "dpc", "algebra", "r1cs-core", "r1cs-std", "gm17", "bench-utils" ] [profile.release] opt-level = 3 diff --git a/r1cs-std/Cargo.toml b/r1cs-std/Cargo.toml new file mode 100644 index 0000000..f4fab26 --- /dev/null +++ b/r1cs-std/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "r1cs-std" +version = "0.1.0" +authors = [ + "Sean Bowe", + "Alessandro Chiesa", + "Matthew Green", + "Ian Miers", + "Pratyush Mishra", + "Howard Wu" +] +description = "A standard library for constraint system gadgets" +homepage = "https://libzexe.org" +repository = "https://github.com/scipr/zexe" +documentation = "https://docs.rs/snark-gadgets/" +keywords = ["zero knowledge", "cryptography", "zkSNARK", "SNARK"] +categories = ["cryptography"] +include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] +license = "MIT/Apache-2.0" +edition = "2018" + +################################# Dependencies ################################ + +[dependencies] +algebra = { path = "../algebra" } +r1cs-core = { path = "../r1cs-core" } +derivative = "1" +radix_trie = "0.1" + +[dev-dependencies] +rand = { version = "0.4" } diff --git a/r1cs-std/LICENSE-APACHE b/r1cs-std/LICENSE-APACHE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/r1cs-std/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/r1cs-std/LICENSE-MIT b/r1cs-std/LICENSE-MIT new file mode 120000 index 0000000..76219eb --- /dev/null +++ b/r1cs-std/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/r1cs-std/src/alloc.rs b/r1cs-std/src/alloc.rs new file mode 100644 index 0000000..e4caa3d --- /dev/null +++ b/r1cs-std/src/alloc.rs @@ -0,0 +1,105 @@ +use algebra::Field; +use r1cs_core::{ConstraintSystem, SynthesisError}; +use std::borrow::Borrow; + +pub trait AllocGadget +where + Self: Sized, + V: ?Sized, +{ + fn alloc>(cs: CS, f: F) -> Result + where + F: FnOnce() -> Result, + T: Borrow; + + fn alloc_checked>(cs: CS, f: F) -> Result + where + F: FnOnce() -> Result, + T: Borrow, + { + Self::alloc(cs, f) + } + + fn alloc_input>(cs: CS, f: F) -> Result + where + F: FnOnce() -> Result, + T: Borrow; + + fn alloc_input_checked>( + cs: CS, + f: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow, + { + Self::alloc_input(cs, f) + } +} + +impl> AllocGadget<[I], ConstraintF> for Vec { + fn alloc>(mut cs: CS, f: F) -> Result + where + F: FnOnce() -> Result, + T: Borrow<[I]>, + { + let mut vec = Vec::new(); + for (i, value) in f()?.borrow().iter().enumerate() { + vec.push(A::alloc(&mut cs.ns(|| format!("value_{}", i)), || { + Ok(value) + })?); + } + Ok(vec) + } + + fn alloc_input>(mut cs: CS, f: F) -> Result + where + F: FnOnce() -> Result, + T: Borrow<[I]>, + { + let mut vec = Vec::new(); + for (i, value) in f()?.borrow().iter().enumerate() { + vec.push(A::alloc_input( + &mut cs.ns(|| format!("value_{}", i)), + || Ok(value), + )?); + } + Ok(vec) + } + + fn alloc_checked>( + mut cs: CS, + f: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow<[I]>, + { + let mut vec = Vec::new(); + for (i, value) in f()?.borrow().iter().enumerate() { + vec.push(A::alloc_checked( + &mut cs.ns(|| format!("value_{}", i)), + || Ok(value), + )?); + } + Ok(vec) + } + + fn alloc_input_checked>( + mut cs: CS, + f: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow<[I]>, + { + let mut vec = Vec::new(); + for (i, value) in f()?.borrow().iter().enumerate() { + vec.push(A::alloc_input_checked( + &mut cs.ns(|| format!("value_{}", i)), + || Ok(value), + )?); + } + Ok(vec) + } +} diff --git a/r1cs-std/src/bits/boolean.rs b/r1cs-std/src/bits/boolean.rs new file mode 100644 index 0000000..de29c90 --- /dev/null +++ b/r1cs-std/src/bits/boolean.rs @@ -0,0 +1,1883 @@ +use algebra::{BitIterator, Field, FpParameters, PrimeField}; + +use crate::prelude::*; +use crate::Assignment; +use r1cs_core::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; +use std::borrow::Borrow; + +/// Represents a variable in the constraint system which is guaranteed +/// to be either zero or one. +#[derive(Copy, Clone, Debug)] +pub struct AllocatedBit { + variable: Variable, + value: Option, +} + +impl AllocatedBit { + pub fn get_value(&self) -> Option { + self.value + } + + pub fn get_variable(&self) -> Variable { + self.variable + } + + /// Performs an XOR operation over the two operands, returning + /// an `AllocatedBit`. + pub fn xor(mut cs: CS, a: &Self, b: &Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let mut result_value = None; + + let result_var = cs.alloc( + || "xor result", + || { + if a.value.get()? ^ b.value.get()? { + result_value = Some(true); + + Ok(ConstraintF::one()) + } else { + result_value = Some(false); + + Ok(ConstraintF::zero()) + } + }, + )?; + + // Constrain (a + a) * (b) = (a + b - c) + // Given that a and b are boolean constrained, if they + // are equal, the only solution for c is 0, and if they + // are different, the only solution for c is 1. + // + // ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c + // (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c + // (1 - ab) * (1 - (1 - a - b + ab)) = c + // (1 - ab) * (a + b - ab) = c + // a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c + // a + b - ab - ab - ab + ab = c + // a + b - 2ab = c + // -2a * b = c - a - b + // 2a * b = a + b - c + // (a + a) * b = a + b - c + cs.enforce( + || "xor constraint", + |lc| lc + a.variable + a.variable, + |lc| lc + b.variable, + |lc| lc + a.variable + b.variable - result_var, + ); + + Ok(AllocatedBit { + variable: result_var, + value: result_value, + }) + } + + /// Performs an AND operation over the two operands, returning + /// an `AllocatedBit`. + pub fn and(mut cs: CS, a: &Self, b: &Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let mut result_value = None; + + let result_var = cs.alloc( + || "and result", + || { + if a.value.get()? & b.value.get()? { + result_value = Some(true); + + Ok(ConstraintF::one()) + } else { + result_value = Some(false); + + Ok(ConstraintF::zero()) + } + }, + )?; + + // Constrain (a) * (b) = (c), ensuring c is 1 iff + // a AND b are both 1. + cs.enforce( + || "and constraint", + |lc| lc + a.variable, + |lc| lc + b.variable, + |lc| lc + result_var, + ); + + Ok(AllocatedBit { + variable: result_var, + value: result_value, + }) + } + + /// Performs an OR operation over the two operands, returning + /// an `AllocatedBit`. + pub fn or(cs: CS, a: &Self, b: &Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + Self::conditionally_select(cs, &Boolean::from(*a), a, b) + } + + /// Calculates `a AND (NOT b)`. + pub fn and_not(mut cs: CS, a: &Self, b: &Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let mut result_value = None; + + let result_var = cs.alloc( + || "and not result", + || { + if a.value.get()? & !b.value.get()? { + result_value = Some(true); + + Ok(ConstraintF::one()) + } else { + result_value = Some(false); + + Ok(ConstraintF::zero()) + } + }, + )?; + + // Constrain (a) * (1 - b) = (c), ensuring c is 1 iff + // a is true and b is false, and otherwise c is 0. + cs.enforce( + || "and not constraint", + |lc| lc + a.variable, + |lc| lc + CS::one() - b.variable, + |lc| lc + result_var, + ); + + Ok(AllocatedBit { + variable: result_var, + value: result_value, + }) + } + + /// Calculates `(NOT a) AND (NOT b)`. + pub fn nor(mut cs: CS, a: &Self, b: &Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let mut result_value = None; + + let result_var = cs.alloc( + || "nor result", + || { + if !a.value.get()? & !b.value.get()? { + result_value = Some(true); + + Ok(ConstraintF::one()) + } else { + result_value = Some(false); + + Ok(ConstraintF::zero()) + } + }, + )?; + + // Constrain (1 - a) * (1 - b) = (c), ensuring c is 1 iff + // a and b are both false, and otherwise c is 0. + cs.enforce( + || "nor constraint", + |lc| lc + CS::one() - a.variable, + |lc| lc + CS::one() - b.variable, + |lc| lc + result_var, + ); + + Ok(AllocatedBit { + variable: result_var, + value: result_value, + }) + } +} + +impl PartialEq for AllocatedBit { + fn eq(&self, other: &Self) -> bool { + !self.value.is_none() && !other.value.is_none() && self.value == other.value + } +} + +impl Eq for AllocatedBit {} + +impl AllocGadget for AllocatedBit { + fn alloc>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow, + { + let mut value = None; + let var = cs.alloc( + || "boolean", + || { + value = Some(*value_gen()?.borrow()); + if value.get()? { + Ok(ConstraintF::one()) + } else { + Ok(ConstraintF::zero()) + } + }, + )?; + + // Constrain: (1 - a) * a = 0 + // This constrains a to be either 0 or 1. + cs.enforce( + || "boolean constraint", + |lc| lc + CS::one() - var, + |lc| lc + var, + |lc| lc, + ); + + Ok(AllocatedBit { + variable: var, + value, + }) + } + + fn alloc_input>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow, + { + let mut value = None; + let var = cs.alloc_input( + || "boolean", + || { + value = Some(*value_gen()?.borrow()); + if value.get()? { + Ok(ConstraintF::one()) + } else { + Ok(ConstraintF::zero()) + } + }, + )?; + + // Constrain: (1 - a) * a = 0 + // This constrains a to be either 0 or 1. + cs.enforce( + || "boolean constraint", + |lc| lc + CS::one() - var, + |lc| lc + var, + |lc| lc, + ); + + Ok(AllocatedBit { + variable: var, + value, + }) + } +} + +impl CondSelectGadget for AllocatedBit { + fn conditionally_select>( + mut cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + 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, ConstraintF::one()), + |lc| lc + first.variable - second.variable, + |lc| lc + result.variable - second.variable, + ); + + Ok(result) + } + + fn cost() -> usize { + 1 + } +} + +/// This is a boolean value which may be either a constant or +/// an interpretation of an `AllocatedBit`. +#[derive(Copy, Clone, Debug)] +pub enum Boolean { + /// Existential view of the boolean variable + Is(AllocatedBit), + /// Negated view of the boolean variable + Not(AllocatedBit), + /// Constant (not an allocated variable) + Constant(bool), +} + +impl Boolean { + pub fn get_value(&self) -> Option { + match *self { + Boolean::Constant(c) => Some(c), + Boolean::Is(ref v) => v.get_value(), + Boolean::Not(ref v) => v.get_value().map(|b| !b), + } + } + + pub fn lc(&self, one: Variable, coeff: ConstraintF) -> LinearCombination { + match *self { + Boolean::Constant(c) => { + if c { + (coeff, one).into() + } else { + LinearCombination::::zero() + } + }, + Boolean::Is(ref v) => (coeff, v.get_variable()).into(), + Boolean::Not(ref v) => { + LinearCombination::::zero() + (coeff, one) - (coeff, v.get_variable()) + }, + } + } + + /// Construct a boolean vector from a vector of u8 + pub fn constant_u8_vec>( + cs: &mut CS, + values: &[u8], + ) -> Vec { + let mut input_bits = vec![]; + for (byte_i, input_byte) in values.into_iter().enumerate() { + for bit_i in (0..8).rev() { + let cs = cs.ns(|| format!("input_bit_gadget {} {}", byte_i, bit_i)); + input_bits.push( + AllocatedBit::alloc(cs, || Ok((input_byte >> bit_i) & 1u8 == 1u8)) + .unwrap() + .into(), + ); + } + } + input_bits + } + + /// Construct a boolean from a known constant + pub fn constant(b: bool) -> Self { + Boolean::Constant(b) + } + + /// Return a negated interpretation of this boolean. + pub fn not(&self) -> Self { + match *self { + Boolean::Constant(c) => Boolean::Constant(!c), + Boolean::Is(ref v) => Boolean::Not(*v), + Boolean::Not(ref v) => Boolean::Is(*v), + } + } + + /// Perform XOR over two boolean operands + pub fn xor<'a, ConstraintF, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + match (a, b) { + (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(*x), + (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.not()), + // a XOR (NOT b) = NOT(a XOR b) + (is @ &Boolean::Is(_), not @ &Boolean::Not(_)) + | (not @ &Boolean::Not(_), is @ &Boolean::Is(_)) => { + Ok(Boolean::xor(cs, is, ¬.not())?.not()) + }, + // a XOR b = (NOT a) XOR (NOT b) + (&Boolean::Is(ref a), &Boolean::Is(ref b)) + | (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { + Ok(Boolean::Is(AllocatedBit::xor(cs, a, b)?)) + }, + } + } + + /// Perform OR over two boolean operands + pub fn or<'a, ConstraintF, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + match (a, b) { + (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(*x), + (&Boolean::Constant(true), _) | (_, &Boolean::Constant(true)) => { + Ok(Boolean::Constant(true)) + }, + // a OR b = NOT ((NOT a) AND b) + (a @ &Boolean::Is(_), b @ &Boolean::Not(_)) + | (b @ &Boolean::Not(_), a @ &Boolean::Is(_)) + | (b @ &Boolean::Not(_), a @ &Boolean::Not(_)) => { + Ok(Boolean::and(cs, &a.not(), &b.not())?.not()) + }, + (&Boolean::Is(ref a), &Boolean::Is(ref b)) => { + AllocatedBit::or(cs, a, b).map(Boolean::from) + }, + } + } + + /// Perform AND over two boolean operands + pub fn and<'a, ConstraintF, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + match (a, b) { + // false AND x is always false + (&Boolean::Constant(false), _) | (_, &Boolean::Constant(false)) => { + Ok(Boolean::Constant(false)) + }, + // true AND x is always x + (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(*x), + // a AND (NOT b) + (&Boolean::Is(ref is), &Boolean::Not(ref not)) + | (&Boolean::Not(ref not), &Boolean::Is(ref is)) => { + Ok(Boolean::Is(AllocatedBit::and_not(cs, is, not)?)) + }, + // (NOT a) AND (NOT b) = a NOR b + (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { + Ok(Boolean::Is(AllocatedBit::nor(cs, a, b)?)) + }, + // a AND b + (&Boolean::Is(ref a), &Boolean::Is(ref b)) => { + Ok(Boolean::Is(AllocatedBit::and(cs, a, b)?)) + }, + } + } + + pub fn kary_and(mut cs: CS, bits: &[Self]) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + assert!(!bits.is_empty()); + let mut bits = bits.iter(); + + let mut cur: Self = *bits.next().unwrap(); + for (i, next) in bits.enumerate() { + cur = Boolean::and(cs.ns(|| format!("AND {}", i)), &cur, next)?; + } + + Ok(cur) + } + + /// Asserts that at least one operand is false. + pub fn enforce_nand(mut cs: CS, bits: &[Self]) -> Result<(), SynthesisError> + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let res = Self::kary_and(&mut cs, bits)?; + + match res { + Boolean::Constant(false) => Ok(()), + Boolean::Constant(true) => Err(SynthesisError::AssignmentMissing), + Boolean::Is(ref res) => { + cs.enforce( + || "enforce nand", + |lc| lc, + |lc| lc, + |lc| lc + res.get_variable(), + ); + + Ok(()) + }, + Boolean::Not(ref res) => { + cs.enforce( + || "enforce nand", + |lc| lc, + |lc| lc, + |lc| lc + CS::one() - res.get_variable(), + ); + + Ok(()) + }, + } + } + + /// Asserts that this bit_gadget representation is "in + /// the field" when interpreted in big endian. + pub fn enforce_in_field( + mut cs: CS, + bits: &[Self], + ) -> Result<(), SynthesisError> + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let mut bits_iter = bits.iter(); + + // b = char() - 1 + let mut b = F::characteristic().to_vec(); + assert_eq!(b[0] % 2, 1); + b[0] -= 1; + + // Runs of ones in r + let mut last_run = Boolean::constant(true); + let mut current_run = vec![]; + + let mut found_one = false; + let mut run_i = 0; + let mut nand_i = 0; + + let char_num_bits = ::Params::MODULUS_BITS as usize; + if bits.len() > char_num_bits { + let num_extra_bits = bits.len() - char_num_bits; + let mut or_result = Boolean::constant(false); + for (i, should_be_zero) in bits[0..num_extra_bits].iter().enumerate() { + or_result = Boolean::or( + &mut cs.ns(|| format!("Check {}-th or", i)), + &or_result, + should_be_zero, + )?; + let _ = bits_iter.next().unwrap(); + } + or_result.enforce_equal( + &mut cs.ns(|| "Check that or of extra bits is zero"), + &Boolean::constant(false), + )?; + } + + for b in BitIterator::new(b) { + // Skip over unset bits at the beginning + found_one |= b; + if !found_one { + continue; + } + + let a = bits_iter.next().unwrap(); + + if b { + // This is part of a run of ones. + current_run.push(a.clone()); + } else { + if current_run.len() > 0 { + // This is the start of a run of zeros, but we need + // to k-ary AND against `last_run` first. + + current_run.push(last_run); + last_run = Self::kary_and(cs.ns(|| format!("run {}", run_i)), ¤t_run)?; + run_i += 1; + current_run.truncate(0); + } + + // If `last_run` is true, `a` must be false, or it would + // not be in the field. + // + // If `last_run` is false, `a` can be true or false. + // + // Ergo, at least one of `last_run` and `a` must be false. + Self::enforce_nand(cs.ns(|| format!("nand {}", nand_i)), &[last_run, *a])?; + nand_i += 1; + } + } + assert!(bits_iter.next().is_none()); + + // We should always end in a "run" of zeros, because + // the characteristic is an odd prime. So, this should + // be empty. + assert!(current_run.is_empty()); + + Ok(()) + } +} + +impl PartialEq for Boolean { + fn eq(&self, other: &Self) -> bool { + use self::Boolean::*; + + match (*self, *other) { + (Is(a), Is(b)) | (Not(a), Not(b)) => a == b, + (Is(a), Not(b)) | (Not(a), Is(b)) => a != b, + (Is(a), Constant(b)) | (Constant(b), Is(a)) => a.value.unwrap() == b, + (Not(a), Constant(b)) | (Constant(b), Not(a)) => a.value.unwrap() != b, + (Constant(a), Constant(b)) => a == b, + } + } +} + +impl Eq for Boolean {} + +impl From for Boolean { + fn from(b: AllocatedBit) -> Boolean { + Boolean::Is(b) + } +} + +impl AllocGadget for Boolean { + fn alloc>(cs: CS, value_gen: F) -> Result + where + F: FnOnce() -> Result, + T: Borrow, + { + AllocatedBit::alloc(cs, value_gen).map(Boolean::from) + } + + fn alloc_input>( + cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow, + { + AllocatedBit::alloc_input(cs, value_gen).map(Boolean::from) + } +} + +impl EqGadget for Boolean {} + +impl ConditionalEqGadget for Boolean { + fn conditional_enforce_equal( + &self, + mut cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> + where + CS: ConstraintSystem, + { + use self::Boolean::*; + let one = CS::one(); + let difference: LinearCombination = match (self, other) { + // 1 - 1 = 0 - 0 = 0 + (Constant(true), Constant(true)) | (Constant(false), Constant(false)) => return Ok(()), + // false != true + (Constant(_), Constant(_)) => Err(SynthesisError::AssignmentMissing)?, + // 1 - a + (Constant(true), Is(a)) | (Is(a), Constant(true)) => { + LinearCombination::zero() + one - a.get_variable() + }, + // a - 0 = a + (Constant(false), Is(a)) | (Is(a), Constant(false)) => { + LinearCombination::zero() + a.get_variable() + }, + // 1 - !a = 1 - (1 - a) = a + (Constant(true), Not(a)) | (Not(a), Constant(true)) => { + LinearCombination::zero() + a.get_variable() + }, + // !a - 0 = !a = 1 - a + (Constant(false), Not(a)) | (Not(a), Constant(false)) => { + LinearCombination::zero() + one - a.get_variable() + }, + // b - a, + (Is(a), Is(b)) => LinearCombination::zero() + b.get_variable() - a.get_variable(), + // !b - a = (1 - b) - a + (Is(a), Not(b)) | (Not(b), Is(a)) => { + LinearCombination::zero() + one - b.get_variable() - a.get_variable() + }, + // !b - !a = (1 - b) - (1 - a) = a - b, + (Not(a), Not(b)) => LinearCombination::zero() + a.get_variable() - b.get_variable(), + }; + + if let Constant(false) = condition { + Ok(()) + } else { + cs.enforce( + || "conditional_equals", + |lc| difference + &lc, + |lc| condition.lc(one, ConstraintF::one()) + &lc, + |lc| lc, + ); + Ok(()) + } + } + + fn cost() -> usize { + 1 + } +} + +impl ToBytesGadget for Boolean { + fn to_bytes>(&self, _cs: CS) -> Result, SynthesisError> { + let mut bits = vec![Boolean::constant(false); 7]; + bits.push(*self); + bits.reverse(); + let value = self.get_value().map(|val| val as u8); + let byte = UInt8 { bits, value }; + Ok(vec![byte]) + } + + /// Additionally checks if the produced list of booleans is 'valid'. + fn to_bytes_strict>( + &self, + cs: CS, + ) -> Result, SynthesisError> { + self.to_bytes(cs) + } +} + +#[cfg(test)] +mod test { + use super::{AllocatedBit, Boolean}; + use crate::{ + test_constraint_system::TestConstraintSystem, + utils::{AllocGadget, ConditionalEqGadget, EqGadget, ToBytesGadget}, + }; + use algebra::{fields::bls12_381::Fr, BitIterator, Field, PrimeField}; + use rand::{Rand, SeedableRng, XorShiftRng}; + use r1cs_core::ConstraintSystem; + use std::str::FromStr; + + #[test] + fn test_boolean_to_byte() { + for val in [true, false].iter() { + let mut cs = TestConstraintSystem::::new(); + let a: Boolean = AllocatedBit::alloc(&mut cs, || Ok(*val)).unwrap().into(); + let bytes = a.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap(); + assert_eq!(bytes.len(), 1); + let byte = &bytes[0]; + assert_eq!(byte.value.unwrap(), *val as u8); + + for (i, bit_gadget) in byte.bits.iter().enumerate() { + assert_eq!( + bit_gadget.get_value().unwrap(), + (byte.value.unwrap() >> i) & 1 == 1 + ); + } + } + } + + #[test] + fn test_allocated_bit() { + let mut cs = TestConstraintSystem::::new(); + + AllocatedBit::alloc(&mut cs, || Ok(true)).unwrap(); + assert!(cs.get("boolean") == Fr::one()); + assert!(cs.is_satisfied()); + cs.set("boolean", Fr::zero()); + assert!(cs.is_satisfied()); + cs.set("boolean", Fr::from_str("2").unwrap()); + assert!(!cs.is_satisfied()); + assert!(cs.which_is_unsatisfied() == Some("boolean constraint")); + } + + #[test] + fn test_xor() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.ns(|| "a"), || Ok(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.ns(|| "b"), || Ok(*b_val)).unwrap(); + let c = AllocatedBit::xor(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), *a_val ^ *b_val); + + assert!(cs.is_satisfied()); + } + } + } + + #[test] + fn test_or() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.ns(|| "a"), || Ok(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.ns(|| "b"), || Ok(*b_val)).unwrap(); + let c = AllocatedBit::or(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), *a_val | *b_val); + + assert!(cs.is_satisfied()); + assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); + assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); + } + } + } + + #[test] + fn test_and() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.ns(|| "a"), || Ok(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.ns(|| "b"), || Ok(*b_val)).unwrap(); + let c = AllocatedBit::and(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), *a_val & *b_val); + + assert!(cs.is_satisfied()); + assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); + assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); + assert!( + cs.get("and result") + == if *a_val & *b_val { + Field::one() + } else { + Field::zero() + } + ); + + // Invert the result and check if the constraint system is still satisfied + cs.set( + "and result", + if *a_val & *b_val { + Field::zero() + } else { + Field::one() + }, + ); + assert!(!cs.is_satisfied()); + } + } + } + + #[test] + fn test_and_not() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.ns(|| "a"), || Ok(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.ns(|| "b"), || Ok(*b_val)).unwrap(); + let c = AllocatedBit::and_not(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), *a_val & !*b_val); + + assert!(cs.is_satisfied()); + assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); + assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); + assert!( + cs.get("and not result") + == if *a_val & !*b_val { + Field::one() + } else { + Field::zero() + } + ); + + // Invert the result and check if the constraint system is still satisfied + cs.set( + "and not result", + if *a_val & !*b_val { + Field::zero() + } else { + Field::one() + }, + ); + assert!(!cs.is_satisfied()); + } + } + } + + #[test] + fn test_nor() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.ns(|| "a"), || Ok(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.ns(|| "b"), || Ok(*b_val)).unwrap(); + let c = AllocatedBit::nor(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), !*a_val & !*b_val); + + assert!(cs.is_satisfied()); + assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); + assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); + assert!( + cs.get("nor result") + == if !*a_val & !*b_val { + Field::one() + } else { + Field::zero() + } + ); + + // Invert the result and check if the constraint system is still satisfied + cs.set( + "nor result", + if !*a_val & !*b_val { + Field::zero() + } else { + Field::one() + }, + ); + assert!(!cs.is_satisfied()); + } + } + } + + #[test] + fn test_enforce_equal() { + for a_bool in [false, true].iter().cloned() { + for b_bool in [false, true].iter().cloned() { + for a_neg in [false, true].iter().cloned() { + for b_neg in [false, true].iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + let mut a: Boolean = AllocatedBit::alloc(cs.ns(|| "a"), || Ok(a_bool)) + .unwrap() + .into(); + let mut b: Boolean = AllocatedBit::alloc(cs.ns(|| "b"), || Ok(b_bool)) + .unwrap() + .into(); + + if a_neg { + a = a.not(); + } + if b_neg { + b = b.not(); + } + + a.enforce_equal(&mut cs, &b).unwrap(); + + assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); + } + } + } + } + } + + #[test] + fn test_conditional_enforce_equal() { + for a_bool in [false, true].iter().cloned() { + for b_bool in [false, true].iter().cloned() { + for a_neg in [false, true].iter().cloned() { + for b_neg in [false, true].iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + // First test if constraint system is satisfied + // when we do want to enforce the condition. + let mut a: Boolean = AllocatedBit::alloc(cs.ns(|| "a"), || Ok(a_bool)) + .unwrap() + .into(); + let mut b: Boolean = AllocatedBit::alloc(cs.ns(|| "b"), || Ok(b_bool)) + .unwrap() + .into(); + + if a_neg { + a = a.not(); + } + if b_neg { + b = b.not(); + } + + a.conditional_enforce_equal(&mut cs, &b, &Boolean::constant(true)) + .unwrap(); + + assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); + + // Now test if constraint system is satisfied even + // when we don't want to enforce the condition. + let mut cs = TestConstraintSystem::::new(); + + let mut a: Boolean = AllocatedBit::alloc(cs.ns(|| "a"), || Ok(a_bool)) + .unwrap() + .into(); + let mut b: Boolean = AllocatedBit::alloc(cs.ns(|| "b"), || Ok(b_bool)) + .unwrap() + .into(); + + if a_neg { + a = a.not(); + } + if b_neg { + b = b.not(); + } + + let false_cond = AllocatedBit::alloc(cs.ns(|| "cond"), || Ok(false)) + .unwrap() + .into(); + a.conditional_enforce_equal(&mut cs, &b, &false_cond) + .unwrap(); + + assert!(cs.is_satisfied()); + } + } + } + } + } + + #[test] + fn test_boolean_negation() { + let mut cs = TestConstraintSystem::::new(); + + let mut b = Boolean::from(AllocatedBit::alloc(&mut cs, || Ok(true)).unwrap()); + + match b { + Boolean::Is(_) => {}, + _ => panic!("unexpected value"), + } + + b = b.not(); + + match b { + Boolean::Not(_) => {}, + _ => panic!("unexpected value"), + } + + b = b.not(); + + match b { + Boolean::Is(_) => {}, + _ => panic!("unexpected value"), + } + + b = Boolean::constant(true); + + match b { + Boolean::Constant(true) => {}, + _ => panic!("unexpected value"), + } + + b = b.not(); + + match b { + Boolean::Constant(false) => {}, + _ => panic!("unexpected value"), + } + + b = b.not(); + + match b { + Boolean::Constant(true) => {}, + _ => panic!("unexpected value"), + } + } + + #[derive(Copy, Clone, Debug)] + enum OperandType { + True, + False, + AllocatedTrue, + AllocatedFalse, + NegatedAllocatedTrue, + NegatedAllocatedFalse, + } + + #[test] + fn test_boolean_xor() { + let variants = [ + OperandType::True, + OperandType::False, + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse, + ]; + + for first_operand in variants.iter().cloned() { + for second_operand in variants.iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + let a; + let b; + + { + let mut dyn_construct = |operand, name| { + let cs = cs.ns(|| name); + + match operand { + OperandType::True => Boolean::constant(true), + OperandType::False => Boolean::constant(false), + OperandType::AllocatedTrue => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(true)).unwrap()) + }, + OperandType::AllocatedFalse => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(false)).unwrap()) + }, + OperandType::NegatedAllocatedTrue => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(true)).unwrap()).not() + }, + OperandType::NegatedAllocatedFalse => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(false)).unwrap()).not() + }, + } + }; + + a = dyn_construct(first_operand, "a"); + b = dyn_construct(second_operand, "b"); + } + + let c = Boolean::xor(&mut cs, &a, &b).unwrap(); + + assert!(cs.is_satisfied()); + + match (first_operand, second_operand, c) { + (OperandType::True, OperandType::True, Boolean::Constant(false)) => {}, + (OperandType::True, OperandType::False, Boolean::Constant(true)) => {}, + (OperandType::True, OperandType::AllocatedTrue, Boolean::Not(_)) => {}, + (OperandType::True, OperandType::AllocatedFalse, Boolean::Not(_)) => {}, + (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Is(_)) => {}, + (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Is(_)) => {}, + + (OperandType::False, OperandType::True, Boolean::Constant(true)) => {}, + (OperandType::False, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::AllocatedTrue, Boolean::Is(_)) => {}, + (OperandType::False, OperandType::AllocatedFalse, Boolean::Is(_)) => {}, + (OperandType::False, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {}, + (OperandType::False, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {}, + + (OperandType::AllocatedTrue, OperandType::True, Boolean::Not(_)) => {}, + (OperandType::AllocatedTrue, OperandType::False, Boolean::Is(_)) => {}, + ( + OperandType::AllocatedTrue, + OperandType::AllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::AllocatedTrue, + OperandType::NegatedAllocatedTrue, + Boolean::Not(ref v), + ) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedTrue, + OperandType::NegatedAllocatedFalse, + Boolean::Not(ref v), + ) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + + (OperandType::AllocatedFalse, OperandType::True, Boolean::Not(_)) => {}, + (OperandType::AllocatedFalse, OperandType::False, Boolean::Is(_)) => {}, + ( + OperandType::AllocatedFalse, + OperandType::AllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::AllocatedFalse, + OperandType::AllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + Boolean::Not(ref v), + ) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedFalse, + Boolean::Not(ref v), + ) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + + (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Is(_)) => {}, + (OperandType::NegatedAllocatedTrue, OperandType::False, Boolean::Not(_)) => {}, + ( + OperandType::NegatedAllocatedTrue, + OperandType::AllocatedTrue, + Boolean::Not(ref v), + ) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::AllocatedFalse, + Boolean::Not(ref v), + ) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + + (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Is(_)) => {}, + (OperandType::NegatedAllocatedFalse, OperandType::False, Boolean::Not(_)) => {}, + ( + OperandType::NegatedAllocatedFalse, + OperandType::AllocatedTrue, + Boolean::Not(ref v), + ) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::AllocatedFalse, + Boolean::Not(ref v), + ) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::NegatedAllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::NegatedAllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + + _ => panic!("this should never be encountered"), + } + } + } + } + + #[test] + fn test_boolean_or() { + let variants = [ + OperandType::True, + OperandType::False, + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse, + ]; + + for first_operand in variants.iter().cloned() { + for second_operand in variants.iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + let a; + let b; + + { + let mut dyn_construct = |operand, name| { + let cs = cs.ns(|| name); + + match operand { + OperandType::True => Boolean::constant(true), + OperandType::False => Boolean::constant(false), + OperandType::AllocatedTrue => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(true)).unwrap()) + }, + OperandType::AllocatedFalse => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(false)).unwrap()) + }, + OperandType::NegatedAllocatedTrue => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(true)).unwrap()).not() + }, + OperandType::NegatedAllocatedFalse => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(false)).unwrap()).not() + }, + } + }; + + a = dyn_construct(first_operand, "a"); + b = dyn_construct(second_operand, "b"); + } + + let c = Boolean::or(&mut cs, &a, &b).unwrap(); + + assert!(cs.is_satisfied()); + + match (first_operand, second_operand, c) { + (OperandType::True, OperandType::True, Boolean::Constant(true)) => {}, + (OperandType::True, OperandType::False, Boolean::Constant(true)) => {}, + (OperandType::True, OperandType::AllocatedTrue, Boolean::Constant(true)) => {}, + (OperandType::True, OperandType::AllocatedFalse, Boolean::Constant(true)) => {}, + ( + OperandType::True, + OperandType::NegatedAllocatedTrue, + Boolean::Constant(true), + ) => {}, + ( + OperandType::True, + OperandType::NegatedAllocatedFalse, + Boolean::Constant(true), + ) => {}, + + (OperandType::False, OperandType::True, Boolean::Constant(true)) => {}, + (OperandType::False, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::AllocatedTrue, Boolean::Is(_)) => {}, + (OperandType::False, OperandType::AllocatedFalse, Boolean::Is(_)) => {}, + (OperandType::False, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {}, + (OperandType::False, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {}, + + (OperandType::AllocatedTrue, OperandType::True, Boolean::Constant(true)) => {}, + (OperandType::AllocatedTrue, OperandType::False, Boolean::Is(_)) => {}, + ( + OperandType::AllocatedTrue, + OperandType::AllocatedTrue, + Boolean::Is(ref v), + ) => { + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + Boolean::Is(ref v), + ) => { + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::AllocatedTrue, + OperandType::NegatedAllocatedTrue, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedTrue, + OperandType::NegatedAllocatedFalse, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + + (OperandType::AllocatedFalse, OperandType::True, Boolean::Constant(true)) => {}, + (OperandType::AllocatedFalse, OperandType::False, Boolean::Is(_)) => {}, + ( + OperandType::AllocatedFalse, + OperandType::AllocatedTrue, + Boolean::Is(ref v), + ) => { + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::AllocatedFalse, + OperandType::AllocatedFalse, + Boolean::Is(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedFalse, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + + ( + OperandType::NegatedAllocatedTrue, + OperandType::True, + Boolean::Constant(true), + ) => {}, + (OperandType::NegatedAllocatedTrue, OperandType::False, Boolean::Not(_)) => {}, + ( + OperandType::NegatedAllocatedTrue, + OperandType::AllocatedTrue, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::AllocatedFalse, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedTrue, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + + ( + OperandType::NegatedAllocatedFalse, + OperandType::True, + Boolean::Constant(true), + ) => {}, + (OperandType::NegatedAllocatedFalse, OperandType::False, Boolean::Not(_)) => {}, + ( + OperandType::NegatedAllocatedFalse, + OperandType::AllocatedTrue, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::AllocatedFalse, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::NegatedAllocatedTrue, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::NegatedAllocatedFalse, + Boolean::Not(ref v), + ) => { + assert_eq!(v.value, Some(false)); + }, + + _ => panic!( + "this should never be encountered, in case: (a = {:?}, b = {:?}, c = {:?})", + a, b, c + ), + } + } + } + } + + #[test] + fn test_boolean_and() { + let variants = [ + OperandType::True, + OperandType::False, + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse, + ]; + + for first_operand in variants.iter().cloned() { + for second_operand in variants.iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + let a; + let b; + + { + let mut dyn_construct = |operand, name| { + let cs = cs.ns(|| name); + + match operand { + OperandType::True => Boolean::constant(true), + OperandType::False => Boolean::constant(false), + OperandType::AllocatedTrue => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(true)).unwrap()) + }, + OperandType::AllocatedFalse => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(false)).unwrap()) + }, + OperandType::NegatedAllocatedTrue => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(true)).unwrap()).not() + }, + OperandType::NegatedAllocatedFalse => { + Boolean::from(AllocatedBit::alloc(cs, || Ok(false)).unwrap()).not() + }, + } + }; + + a = dyn_construct(first_operand, "a"); + b = dyn_construct(second_operand, "b"); + } + + let c = Boolean::and(&mut cs, &a, &b).unwrap(); + + assert!(cs.is_satisfied()); + + match (first_operand, second_operand, c) { + (OperandType::True, OperandType::True, Boolean::Constant(true)) => {}, + (OperandType::True, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::True, OperandType::AllocatedTrue, Boolean::Is(_)) => {}, + (OperandType::True, OperandType::AllocatedFalse, Boolean::Is(_)) => {}, + (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {}, + (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {}, + + (OperandType::False, OperandType::True, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::AllocatedTrue, Boolean::Constant(false)) => { + }, + (OperandType::False, OperandType::AllocatedFalse, Boolean::Constant(false)) => { + }, + ( + OperandType::False, + OperandType::NegatedAllocatedTrue, + Boolean::Constant(false), + ) => {}, + ( + OperandType::False, + OperandType::NegatedAllocatedFalse, + Boolean::Constant(false), + ) => {}, + + (OperandType::AllocatedTrue, OperandType::True, Boolean::Is(_)) => {}, + (OperandType::AllocatedTrue, OperandType::False, Boolean::Constant(false)) => { + }, + ( + OperandType::AllocatedTrue, + OperandType::AllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedTrue, + OperandType::NegatedAllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedTrue, + OperandType::NegatedAllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and not result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + + (OperandType::AllocatedFalse, OperandType::True, Boolean::Is(_)) => {}, + (OperandType::AllocatedFalse, OperandType::False, Boolean::Constant(false)) => { + }, + ( + OperandType::AllocatedFalse, + OperandType::AllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedFalse, + OperandType::AllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + + (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Not(_)) => {}, + ( + OperandType::NegatedAllocatedTrue, + OperandType::False, + Boolean::Constant(false), + ) => {}, + ( + OperandType::NegatedAllocatedTrue, + OperandType::AllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::AllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("nor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("nor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + + (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Not(_)) => {}, + ( + OperandType::NegatedAllocatedFalse, + OperandType::False, + Boolean::Constant(false), + ) => {}, + ( + OperandType::NegatedAllocatedFalse, + OperandType::AllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and not result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::AllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::NegatedAllocatedTrue, + Boolean::Is(ref v), + ) => { + assert!(cs.get("nor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + ( + OperandType::NegatedAllocatedFalse, + OperandType::NegatedAllocatedFalse, + Boolean::Is(ref v), + ) => { + assert!(cs.get("nor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + + _ => { + panic!( + "unexpected behavior at {:?} AND {:?}", + first_operand, second_operand + ); + }, + } + } + } + } + + #[test] + fn test_enforce_in_field() { + { + let mut cs = TestConstraintSystem::::new(); + + let mut bits = vec![]; + for (i, b) in BitIterator::new(Fr::characteristic()).skip(1).enumerate() { + bits.push(Boolean::from( + AllocatedBit::alloc(cs.ns(|| format!("bit_gadget {}", i)), || Ok(b)).unwrap(), + )); + } + + Boolean::enforce_in_field::<_, _, Fr>(&mut cs, &bits).unwrap(); + + assert!(!cs.is_satisfied()); + } + + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let r = Fr::rand(&mut rng); + let mut cs = TestConstraintSystem::::new(); + + let mut bits = vec![]; + for (i, b) in BitIterator::new(r.into_repr()).skip(1).enumerate() { + bits.push(Boolean::from( + AllocatedBit::alloc(cs.ns(|| format!("bit_gadget {}", i)), || Ok(b)).unwrap(), + )); + } + + Boolean::enforce_in_field::<_, _, Fr>(&mut cs, &bits).unwrap(); + + assert!(cs.is_satisfied()); + } + + // for _ in 0..1000 { + // // Sample a random element not in the field + // let r = loop { + // let mut a = Fr::rand(&mut rng).into_repr(); + // let b = Fr::rand(&mut rng).into_repr(); + + // a.add_nocarry(&b); + // // we're shaving off the high bit_gadget later + // a.as_mut()[3] &= 0x7fffffffffffffff; + // if Fr::from_repr(a).is_err() { + // break a; + // } + // }; + + // let mut cs = TestConstraintSystem::::new(); + + // let mut bits = vec![]; + // for (i, b) in BitIterator::new(r).skip(1).enumerate() { + // bits.push(Boolean::from( + // AllocatedBit::alloc(cs.ns(|| format!("bit_gadget {}", i)), + // Some(b)) .unwrap(), + // )); + // } + + // Boolean::enforce_in_field::<_, _, Fr>(&mut cs, &bits).unwrap(); + + // assert!(!cs.is_satisfied()); + // } + } + + #[test] + fn test_enforce_nand() { + { + let mut cs = TestConstraintSystem::::new(); + + assert!(Boolean::enforce_nand(&mut cs, &[Boolean::constant(false)]).is_ok()); + assert!(Boolean::enforce_nand(&mut cs, &[Boolean::constant(true)]).is_err()); + } + + for i in 1..5 { + // with every possible assignment for them + for mut b in 0..(1 << i) { + // with every possible negation + for mut n in 0..(1 << i) { + let mut cs = TestConstraintSystem::::new(); + + let mut expected = true; + + let mut bits = vec![]; + for j in 0..i { + expected &= b & 1 == 1; + + if n & 1 == 1 { + bits.push(Boolean::from( + AllocatedBit::alloc(cs.ns(|| format!("bit_gadget {}", j)), || { + Ok(b & 1 == 1) + }) + .unwrap(), + )); + } else { + bits.push( + Boolean::from( + AllocatedBit::alloc( + cs.ns(|| format!("bit_gadget {}", j)), + || Ok(b & 1 == 0), + ) + .unwrap(), + ) + .not(), + ); + } + + b >>= 1; + n >>= 1; + } + + let expected = !expected; + + Boolean::enforce_nand(&mut cs, &bits).unwrap(); + + if expected { + assert!(cs.is_satisfied()); + } else { + assert!(!cs.is_satisfied()); + } + } + } + } + } + + #[test] + fn test_kary_and() { + // test different numbers of operands + for i in 1..15 { + // with every possible assignment for them + for mut b in 0..(1 << i) { + let mut cs = TestConstraintSystem::::new(); + + let mut expected = true; + + let mut bits = vec![]; + for j in 0..i { + expected &= b & 1 == 1; + + bits.push(Boolean::from( + AllocatedBit::alloc(cs.ns(|| format!("bit_gadget {}", j)), || { + Ok(b & 1 == 1) + }) + .unwrap(), + )); + b >>= 1; + } + + let r = Boolean::kary_and(&mut cs, &bits).unwrap(); + + assert!(cs.is_satisfied()); + + match r { + Boolean::Is(ref r) => { + assert_eq!(r.value.unwrap(), expected); + }, + _ => unreachable!(), + } + } + } + } +} diff --git a/r1cs-std/src/bits/mod.rs b/r1cs-std/src/bits/mod.rs new file mode 100644 index 0000000..3540bed --- /dev/null +++ b/r1cs-std/src/bits/mod.rs @@ -0,0 +1,84 @@ +use crate::bits::{boolean::Boolean, uint8::UInt8}; +use algebra::Field; +use r1cs_core::{ConstraintSystem, SynthesisError}; + +pub mod boolean; +pub mod uint32; +pub mod uint8; + +pub trait ToBitsGadget { + fn to_bits>(&self, cs: CS) -> Result, SynthesisError>; + + /// Additionally checks if the produced list of booleans is 'valid'. + fn to_bits_strict>( + &self, + cs: CS, + ) -> Result, SynthesisError>; +} + +impl ToBitsGadget for Boolean { + fn to_bits>(&self, _: CS) -> Result, SynthesisError> { + Ok(vec![self.clone()]) + } + + fn to_bits_strict>( + &self, + _: CS, + ) -> Result, SynthesisError> { + Ok(vec![self.clone()]) + } +} + +impl ToBitsGadget for [Boolean] { + fn to_bits>(&self, _cs: CS) -> Result, SynthesisError> { + Ok(self.to_vec()) + } + + fn to_bits_strict>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { + Ok(self.to_vec()) + } +} +impl ToBitsGadget for Vec { + fn to_bits>(&self, _cs: CS) -> Result, SynthesisError> { + Ok(self.clone()) + } + + fn to_bits_strict>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { + Ok(self.clone()) + } +} + +impl ToBitsGadget for [UInt8] { + fn to_bits>(&self, _cs: CS) -> Result, SynthesisError> { + let mut result = Vec::with_capacity(&self.len() * 8); + for byte in self { + result.extend_from_slice(&byte.into_bits_le()); + } + Ok(result) + } + + fn to_bits_strict>( + &self, + cs: CS, + ) -> Result, SynthesisError> { + self.to_bits(cs) + } +} + +pub trait ToBytesGadget { + fn to_bytes>(&self, cs: CS) -> Result, SynthesisError>; + + /// Additionally checks if the produced list of booleans is 'valid'. + fn to_bytes_strict>( + &self, + cs: CS, + ) -> Result, SynthesisError>; +} + + diff --git a/r1cs-std/src/bits/uint32.rs b/r1cs-std/src/bits/uint32.rs new file mode 100644 index 0000000..7a3b3dc --- /dev/null +++ b/r1cs-std/src/bits/uint32.rs @@ -0,0 +1,534 @@ +use algebra::{FpParameters, PrimeField, Field}; + +use r1cs_core::{ConstraintSystem, LinearCombination, SynthesisError}; + +use crate::boolean::{AllocatedBit, Boolean}; +use crate::Assignment; +use crate::prelude::*; + +/// Represents an interpretation of 32 `Boolean` objects as an +/// unsigned integer. +#[derive(Clone, Debug)] +pub struct UInt32 { + // Least significant bit_gadget first + bits: Vec, + value: Option, +} + +impl UInt32 { + /// Construct a constant `UInt32` from a `u32` + pub fn constant(value: u32) -> Self { + let mut bits = Vec::with_capacity(32); + + let mut tmp = value; + for _ in 0..32 { + if tmp & 1 == 1 { + bits.push(Boolean::constant(true)) + } else { + bits.push(Boolean::constant(false)) + } + + tmp >>= 1; + } + + UInt32 { + bits, + value: Some(value), + } + } + + /// Allocate a `UInt32` in the constraint system + pub fn alloc(mut cs: CS, value: Option) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let values = match value { + Some(mut val) => { + let mut v = Vec::with_capacity(32); + + for _ in 0..32 { + v.push(Some(val & 1 == 1)); + val >>= 1; + } + + v + }, + None => vec![None; 32], + }; + + let bits = values + .into_iter() + .enumerate() + .map(|(i, v)| { + Ok(Boolean::from(AllocatedBit::alloc( + cs.ns(|| format!("allocated bit_gadget {}", i)), + || v.get(), + )?)) + }) + .collect::, SynthesisError>>()?; + + Ok(UInt32 { bits, value }) + } + + /// Turns this `UInt32` into its little-endian byte order representation. + pub fn to_bits_le(&self) -> Vec { + self.bits.clone() + } + + /// Converts a little-endian byte order representation of bits into a + /// `UInt32`. + pub fn from_bits_le(bits: &[Boolean]) -> Self { + assert_eq!(bits.len(), 32); + + let bits = bits.to_vec(); + + let mut value = Some(0u32); + for b in bits.iter().rev() { + value.as_mut().map(|v| *v <<= 1); + + match b { + &Boolean::Constant(b) => { + 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) => {}, + None => value = None, + }, + &Boolean::Not(ref b) => match b.get_value() { + Some(false) => { + value.as_mut().map(|v| *v |= 1); + }, + Some(true) => {}, + None => value = None, + }, + } + } + + Self { value, bits } + } + + pub fn rotr(&self, by: usize) -> Self { + let by = by % 32; + + let new_bits = self + .bits + .iter() + .skip(by) + .chain(self.bits.iter()) + .take(32) + .cloned() + .collect(); + + UInt32 { + bits: new_bits, + value: self.value.map(|v| v.rotate_right(by as u32)), + } + } + + /// XOR this `UInt32` with another `UInt32` + pub fn xor(&self, mut cs: CS, other: &Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let new_value = match (self.value, other.value) { + (Some(a), Some(b)) => Some(a ^ b), + _ => None, + }; + + let bits = self + .bits + .iter() + .zip(other.bits.iter()) + .enumerate() + .map(|(i, (a, b))| Boolean::xor(cs.ns(|| format!("xor of bit_gadget {}", i)), a, b)) + .collect::>()?; + + Ok(UInt32 { + bits, + value: new_value, + }) + } + + /// Perform modular addition of several `UInt32` objects. + pub fn addmany(mut cs: CS, operands: &[Self]) -> Result + where + ConstraintF: PrimeField, + CS: ConstraintSystem, + { + // Make some arbitrary bounds for ourselves to avoid overflows + // in the scalar field + assert!(ConstraintF::Params::MODULUS_BITS >= 64); + assert!(operands.len() >= 2); // Weird trivial cases that should never happen + assert!(operands.len() <= 10); + + // Compute the maximum value of the sum so we allocate enough bits for + // the result + let mut max_value = (operands.len() as u64) * u64::from(u32::max_value()); + + // Keep track of the resulting value + let mut result_value = Some(0u64); + + // This is a linear combination that we will enforce to be "zero" + let mut lc = LinearCombination::zero(); + + let mut all_constants = true; + + // Iterate over the operands + for op in operands { + // Accumulate the value + match op.value { + Some(val) => { + result_value.as_mut().map(|v| *v += u64::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 + // the linear combination + let mut coeff = ConstraintF::one(); + for bit in &op.bits { + match *bit { + Boolean::Is(ref bit) => { + all_constants = false; + + // Add coeff * bit_gadget + lc = 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 = lc + (coeff, CS::one()); + } + }, + } + + coeff.double_in_place(); + } + } + + // The value of the actual result is modulo 2^32 + let modular_value = result_value.map(|v| v as u32); + + if all_constants && modular_value.is_some() { + // We can just return a constant, rather than + // unpacking the result into allocated bits. + + return Ok(UInt32::constant(modular_value.unwrap())); + } + + // Storage area for the resulting bits + let mut result_bits = vec![]; + + // Allocate each bit_gadget of the result + let mut coeff = ConstraintF::one(); + let mut i = 0; + while max_value != 0 { + // Allocate the bit_gadget + let b = AllocatedBit::alloc(cs.ns(|| format!("result bit_gadget {}", i)), || { + result_value.map(|v| (v >> i) & 1 == 1).get() + })?; + + // Subtract this bit_gadget from the linear combination to ensure the sums + // balance out + lc = lc - (coeff, b.get_variable()); + + result_bits.push(b.into()); + + max_value >>= 1; + i += 1; + coeff.double_in_place(); + } + + // Enforce that the linear combination equals zero + cs.enforce(|| "modular addition", |lc| lc, |lc| lc, |_| lc); + + // Discard carry bits that we don't care about + result_bits.truncate(32); + + Ok(UInt32 { + bits: result_bits, + value: modular_value, + }) + } +} + +impl ToBytesGadget for UInt32 { + #[inline] + fn to_bytes>(&self, _cs: CS) -> Result, SynthesisError> { + let value_chunks = match self.value.map(|val| { + use algebra::bytes::ToBytes; + let mut bytes = [0u8; 4]; + val.write(bytes.as_mut()).unwrap(); + bytes + }) { + Some(chunks) => [ + Some(chunks[0]), + Some(chunks[1]), + Some(chunks[2]), + Some(chunks[3]), + ], + None => [None, None, None, None], + }; + let mut bytes = Vec::new(); + for (i, chunk8) in self.to_bits_le().chunks(8).into_iter().enumerate() { + let byte = UInt8 { + bits: chunk8.to_vec(), + value: value_chunks[i], + }; + bytes.push(byte); + } + + Ok(bytes) + } + + fn to_bytes_strict>( + &self, + cs: CS, + ) -> Result, SynthesisError> { + self.to_bytes(cs) + } +} + +impl PartialEq for UInt32 { + fn eq(&self, other: &Self) -> bool { + !self.value.is_none() && !other.value.is_none() && self.value == other.value + } +} + +impl Eq for UInt32 {} + +impl ConditionalEqGadget for UInt32 { + fn conditional_enforce_equal>( + &self, + mut cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + for (i, (a, b)) in self.bits.iter().zip(&other.bits).enumerate() { + a.conditional_enforce_equal( + &mut cs.ns(|| format!("uint32_equal_{}", i)), + b, + condition, + )?; + } + Ok(()) + } + + fn cost() -> usize { + 32 * >::cost() + } +} + +#[cfg(test)] +mod test { + use super::UInt32; + use crate::{bits::boolean::Boolean, test_constraint_system::TestConstraintSystem}; + use algebra::fields::{bls12_381::Fr, Field}; + use rand::{Rng, SeedableRng, XorShiftRng}; + use r1cs_core::ConstraintSystem; + + #[test] + fn test_uint32_from_bits() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let v = (0..32) + .map(|_| Boolean::constant(rng.gen())) + .collect::>(); + + let b = UInt32::from_bits_le(&v); + + for (i, bit_gadget) in b.bits.iter().enumerate() { + match bit_gadget { + &Boolean::Constant(bit_gadget) => { + assert!(bit_gadget == ((b.value.unwrap() >> i) & 1 == 1)); + }, + _ => unreachable!(), + } + } + + let expected_to_be_same = b.to_bits_le(); + + 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)) => {}, + _ => unreachable!(), + } + } + } + } + + #[test] + fn test_uint32_xor() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u32 = rng.gen(); + let b: u32 = rng.gen(); + let c: u32 = rng.gen(); + + let mut expected = a ^ b ^ c; + + let a_bit = UInt32::alloc(cs.ns(|| "a_bit"), Some(a)).unwrap(); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::alloc(cs.ns(|| "c_bit"), Some(c)).unwrap(); + + let r = a_bit.xor(cs.ns(|| "first xor"), &b_bit).unwrap(); + let r = r.xor(cs.ns(|| "second xor"), &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + 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; + } + } + } + + #[test] + fn test_uint32_addmany_constants() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u32 = rng.gen(); + let b: u32 = rng.gen(); + let c: u32 = rng.gen(); + + let a_bit = UInt32::constant(a); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::constant(c); + + let mut expected = a.wrapping_add(b).wrapping_add(c); + + let r = UInt32::addmany(cs.ns(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match b { + &Boolean::Is(_) => panic!(), + &Boolean::Not(_) => panic!(), + &Boolean::Constant(b) => { + assert!(b == (expected & 1 == 1)); + }, + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_addmany() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u32 = rng.gen(); + let b: u32 = rng.gen(); + let c: u32 = rng.gen(); + let d: u32 = rng.gen(); + + let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); + + let a_bit = UInt32::alloc(cs.ns(|| "a_bit"), Some(a)).unwrap(); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::constant(c); + let d_bit = UInt32::alloc(cs.ns(|| "d_bit"), Some(d)).unwrap(); + + let r = a_bit.xor(cs.ns(|| "xor"), &b_bit).unwrap(); + let r = UInt32::addmany(cs.ns(|| "addition"), &[r, c_bit, d_bit]).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + 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!(), + } + + expected >>= 1; + } + + // Flip a bit_gadget and see if the addition constraint still works + if cs.get("addition/result bit_gadget 0/boolean").is_zero() { + cs.set("addition/result bit_gadget 0/boolean", Field::one()); + } else { + cs.set("addition/result bit_gadget 0/boolean", Field::zero()); + } + + assert!(!cs.is_satisfied()); + } + } + + #[test] + fn test_uint32_rotr() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut num = rng.gen(); + + let a = UInt32::constant(num); + + for i in 0..32 { + let b = a.rotr(i); + + assert!(b.value.unwrap() == num); + + let mut tmp = num; + for b in &b.bits { + match b { + &Boolean::Constant(b) => { + assert_eq!(b, tmp & 1 == 1); + }, + _ => unreachable!(), + } + + tmp >>= 1; + } + + num = num.rotate_right(1); + } + } +} diff --git a/r1cs-std/src/bits/uint8.rs b/r1cs-std/src/bits/uint8.rs new file mode 100644 index 0000000..61eda09 --- /dev/null +++ b/r1cs-std/src/bits/uint8.rs @@ -0,0 +1,399 @@ +use algebra::{ToConstraintField, FpParameters, Field, PrimeField}; + +use r1cs_core::{ConstraintSystem, SynthesisError}; + +use crate::boolean::AllocatedBit; +use crate::fields::fp::FpGadget; +use crate::prelude::*; +use crate::Assignment; +use std::borrow::Borrow; + +/// Represents an interpretation of 8 `Boolean` objects as an +/// unsigned integer. +#[derive(Clone, Debug)] +pub struct UInt8 { + // Least significant bit_gadget first + pub(crate) bits: Vec, + pub(crate) value: Option, +} + +impl UInt8 { + pub fn get_value(&self) -> Option { + self.value + } + + /// Construct a constant vector of `UInt8` from a vector of `u8` + pub fn constant_vec(values: &[u8]) -> Vec { + let mut result = Vec::new(); + for value in values { + result.push(UInt8::constant(*value)); + } + result + } + + /// Construct a constant `UInt8` from a `u8` + pub fn constant(value: u8) -> Self { + let mut bits = Vec::with_capacity(8); + + let mut tmp = value; + for _ in 0..8 { + // If last bit is one, push one. + if tmp & 1 == 1 { + bits.push(Boolean::constant(true)) + } else { + bits.push(Boolean::constant(false)) + } + + tmp >>= 1; + } + + Self { + bits, + value: Some(value), + } + } + + pub fn alloc_vec(mut cs: CS, values: &[T]) -> Result, SynthesisError> + where + ConstraintF: Field, + CS: ConstraintSystem, + T: Into> + Copy, + { + let mut output_vec = Vec::with_capacity(values.len()); + for (i, value) in values.into_iter().enumerate() { + let byte: Option = Into::into(*value); + let alloc_byte = Self::alloc(&mut cs.ns(|| format!("byte_{}", i)), || byte.get())?; + output_vec.push(alloc_byte); + } + Ok(output_vec) + } + + /// Allocates a vector of `u8`'s by first converting (chunks of) them to + /// `ConstraintF` elements, (thus reducing the number of input allocations), and + /// then converts this list of `ConstraintF` gadgets back into bytes. + pub fn alloc_input_vec(mut cs: CS, values: &[u8]) -> Result, SynthesisError> + where + ConstraintF: PrimeField, + CS: ConstraintSystem, + { + let values_len = values.len(); + let field_elements: Vec = ToConstraintField::::to_field_elements(values).unwrap(); + + let max_size = 8 * (ConstraintF::Params::CAPACITY / 8) as usize; + let mut allocated_bits = Vec::new(); + for (i, field_element) in field_elements.into_iter().enumerate() { + let fe = FpGadget::alloc_input(&mut cs.ns(|| format!("Field element {}", i)), || { + Ok(field_element) + })?; + let mut fe_bits = fe.to_bits(cs.ns(|| format!("Convert fe to bits {}", i)))?; + // FpGadget::to_bits outputs a big-endian binary representation of + // fe_gadget's value, so we have to reverse it to get the little-endian + // form. + fe_bits.reverse(); + + // Remove the most significant bit, because we know it should be zero + // because `values.to_field_elements()` only + // packs field elements up to the penultimate bit. + // That is, the most significant bit (`ConstraintF::NUM_BITS`-th bit) is + // unset, so we can just pop it off. + allocated_bits.extend_from_slice(&fe_bits[0..max_size]); + } + + // Chunk up slices of 8 bit into bytes. + Ok(allocated_bits[0..8 * values_len] + .chunks(8) + .map(Self::from_bits_le) + .collect()) + } + + /// Turns this `UInt8` into its little-endian byte order representation. + /// LSB-first means that we can easily get the corresponding field element + /// via double and add. + pub fn into_bits_le(&self) -> Vec { + self.bits.iter().cloned().collect() + } + + /// Converts a little-endian byte order representation of bits into a + /// `UInt8`. + pub fn from_bits_le(bits: &[Boolean]) -> Self { + assert_eq!(bits.len(), 8); + + let bits = bits.to_vec(); + + let mut value = Some(0u8); + for b in bits.iter().rev() { + value.as_mut().map(|v| *v <<= 1); + + match *b { + Boolean::Constant(b) => { + 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) => {}, + None => value = None, + }, + Boolean::Not(ref b) => match b.get_value() { + Some(false) => { + value.as_mut().map(|v| *v |= 1); + }, + Some(true) => {}, + None => value = None, + }, + } + } + + Self { value, bits } + } + + /// XOR this `UInt8` with another `UInt8` + pub fn xor(&self, mut cs: CS, other: &Self) -> Result + where + ConstraintF: Field, + CS: ConstraintSystem, + { + let new_value = match (self.value, other.value) { + (Some(a), Some(b)) => Some(a ^ b), + _ => None, + }; + + let bits = self + .bits + .iter() + .zip(other.bits.iter()) + .enumerate() + .map(|(i, (a, b))| Boolean::xor(cs.ns(|| format!("xor of bit_gadget {}", i)), a, b)) + .collect::>()?; + + Ok(Self { + bits, + value: new_value, + }) + } +} + +impl PartialEq for UInt8 { + fn eq(&self, other: &Self) -> bool { + !self.value.is_none() && !other.value.is_none() && self.value == other.value + } +} + +impl Eq for UInt8 {} + +impl ConditionalEqGadget for UInt8 { + fn conditional_enforce_equal>( + &self, + mut cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + for (i, (a, b)) in self.bits.iter().zip(&other.bits).enumerate() { + a.conditional_enforce_equal( + &mut cs.ns(|| format!("UInt8 equality check for {}-th bit", i)), + b, + condition, + )?; + } + Ok(()) + } + + fn cost() -> usize { + 8 * >::cost() + } +} + +impl EqGadget for UInt8 {} + +impl AllocGadget for UInt8 { + fn alloc>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow, + { + let value = value_gen().map(|val| *val.borrow()); + let values = match value { + Ok(mut val) => { + let mut v = Vec::with_capacity(8); + + for _ in 0..8 { + v.push(Some(val & 1 == 1)); + val >>= 1; + } + + v + }, + _ => vec![None; 8], + }; + + let bits = values + .into_iter() + .enumerate() + .map(|(i, v)| { + Ok(Boolean::from(AllocatedBit::alloc( + &mut cs.ns(|| format!("allocated bit_gadget {}", i)), + || v.ok_or(SynthesisError::AssignmentMissing), + )?)) + }) + .collect::, SynthesisError>>()?; + + Ok(Self { + bits, + value: value.ok(), + }) + } + + fn alloc_input>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow, + { + let value = value_gen().map(|val| *val.borrow()); + let values = match value { + Ok(mut val) => { + let mut v = Vec::with_capacity(8); + for _ in 0..8 { + v.push(Some(val & 1 == 1)); + val >>= 1; + } + + v + }, + _ => vec![None; 8], + }; + + let bits = values + .into_iter() + .enumerate() + .map(|(i, v)| { + Ok(Boolean::from(AllocatedBit::alloc_input( + &mut cs.ns(|| format!("allocated bit_gadget {}", i)), + || v.ok_or(SynthesisError::AssignmentMissing), + )?)) + }) + .collect::, SynthesisError>>()?; + + Ok(Self { + bits, + value: value.ok(), + }) + } +} + +#[cfg(test)] +mod test { + use super::UInt8; + use crate::{ + bits::boolean::Boolean, test_constraint_system::TestConstraintSystem, utils::AllocGadget, + }; + use algebra::fields::bls12_381::Fr; + use rand::{Rng, SeedableRng, XorShiftRng}; + use r1cs_core::ConstraintSystem; + + #[test] + fn test_uint8_from_bits_to_bits() { + let mut cs = TestConstraintSystem::::new(); + let byte_val = 0b01110001; + let byte = UInt8::alloc(cs.ns(|| "alloc value"), || Ok(byte_val)).unwrap(); + let bits = byte.into_bits_le(); + for (i, bit) in bits.iter().enumerate() { + assert_eq!(bit.get_value().unwrap(), (byte_val >> i) & 1 == 1) + } + } + + #[test] + fn test_uint8_alloc_input_vec() { + let mut cs = TestConstraintSystem::::new(); + let byte_vals = (64u8..128u8).into_iter().collect::>(); + let bytes = UInt8::alloc_input_vec(cs.ns(|| "alloc value"), &byte_vals).unwrap(); + for (native_byte, gadget_byte) in byte_vals.into_iter().zip(bytes) { + let bits = gadget_byte.into_bits_le(); + for (i, bit) in bits.iter().enumerate() { + assert_eq!(bit.get_value().unwrap(), (native_byte >> i) & 1 == 1) + } + } + } + + #[test] + fn test_uint8_from_bits() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let v = (0..8) + .map(|_| Boolean::constant(rng.gen())) + .collect::>(); + + let b = UInt8::from_bits_le(&v); + + for (i, bit_gadget) in b.bits.iter().enumerate() { + match bit_gadget { + &Boolean::Constant(bit_gadget) => { + assert!(bit_gadget == ((b.value.unwrap() >> i) & 1 == 1)); + }, + _ => unreachable!(), + } + } + + let expected_to_be_same = b.into_bits_le(); + + 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)) => {}, + _ => unreachable!(), + } + } + } + } + + #[test] + fn test_uint8_xor() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u8 = rng.gen(); + let b: u8 = rng.gen(); + let c: u8 = rng.gen(); + + let mut expected = a ^ b ^ c; + + let a_bit = UInt8::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap(); + let b_bit = UInt8::constant(b); + let c_bit = UInt8::alloc(cs.ns(|| "c_bit"), || Ok(c)).unwrap(); + + let r = a_bit.xor(cs.ns(|| "first xor"), &b_bit).unwrap(); + let r = r.xor(cs.ns(|| "second xor"), &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + 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; + } + } + } +} diff --git a/r1cs-std/src/eq.rs b/r1cs-std/src/eq.rs new file mode 100644 index 0000000..301fbfc --- /dev/null +++ b/r1cs-std/src/eq.rs @@ -0,0 +1,137 @@ +use crate::prelude::*; +use r1cs_core::{ConstraintSystem, SynthesisError}; +use algebra::Field; + +/// If `condition == 1`, then enforces that `self` and `other` are equal; +/// otherwise, it doesn't enforce anything. +pub trait ConditionalEqGadget: Eq { + fn conditional_enforce_equal>( + &self, + cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError>; + + fn cost() -> usize; +} +impl, ConstraintF: Field> ConditionalEqGadget for [T] { + fn conditional_enforce_equal>( + &self, + mut cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + for (i, (a, b)) in self.iter().zip(other.iter()).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + a.conditional_enforce_equal(&mut cs, b, condition)?; + } + Ok(()) + } + + fn cost() -> usize { + unimplemented!() + } +} + +pub trait EqGadget: Eq +where + Self: ConditionalEqGadget, +{ + fn enforce_equal>( + &self, + cs: CS, + other: &Self, + ) -> Result<(), SynthesisError> { + self.conditional_enforce_equal(cs, other, &Boolean::constant(true)) + } + + fn cost() -> usize { + >::cost() + } +} + +impl, ConstraintF: Field> EqGadget for [T] {} + +pub trait NEqGadget: Eq { + fn enforce_not_equal>( + &self, + cs: CS, + other: &Self, + ) -> Result<(), SynthesisError>; + + fn cost() -> usize; +} + +pub trait OrEqualsGadget +where + Self: Sized, +{ + fn enforce_equal_or>( + cs: CS, + cond: &Boolean, + var: &Self, + first: &Self, + second: &Self, + ) -> Result<(), SynthesisError>; + + fn cost() -> usize; +} + +impl> OrEqualsGadget for T { + fn enforce_equal_or>( + cs: CS, + cond: &Boolean, + var: &Self, + first: &Self, + second: &Self, + ) -> Result<(), SynthesisError> { + Self::conditional_enforce_equal_or(cs, cond, var, first, second, &Boolean::Constant(true)) + } + + fn cost() -> usize { + >::cost() + } +} + +pub trait ConditionalOrEqualsGadget +where + Self: Sized, +{ + fn conditional_enforce_equal_or>( + cs: CS, + cond: &Boolean, + var: &Self, + first: &Self, + second: &Self, + should_enforce: &Boolean, + ) -> Result<(), SynthesisError>; + + fn cost() -> usize; +} + +impl + CondSelectGadget> + ConditionalOrEqualsGadget for T +{ + fn conditional_enforce_equal_or>( + mut cs: CS, + cond: &Boolean, + var: &Self, + first: &Self, + second: &Self, + should_enforce: &Boolean, + ) -> Result<(), SynthesisError> { + let match_opt = Self::conditionally_select( + &mut cs.ns(|| "conditional_select_in_or"), + cond, + first, + second, + )?; + var.conditional_enforce_equal(&mut cs.ns(|| "equals_in_or"), &match_opt, should_enforce) + } + + fn cost() -> usize { + >::cost() + >::cost() + } +} + + diff --git a/r1cs-std/src/fields/bls12_377.rs b/r1cs-std/src/fields/bls12_377.rs new file mode 100644 index 0000000..85fcf54 --- /dev/null +++ b/r1cs-std/src/fields/bls12_377.rs @@ -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; +pub type Fq2Gadget = Fp2Gadget; +pub type Fq6Gadget = Fp6Gadget; +pub type Fq12Gadget = Fp12Gadget; diff --git a/r1cs-std/src/fields/edwards_bls12.rs b/r1cs-std/src/fields/edwards_bls12.rs new file mode 100644 index 0000000..c66832f --- /dev/null +++ b/r1cs-std/src/fields/edwards_bls12.rs @@ -0,0 +1,4 @@ +use algebra::fields::edwards_bls12::fq::Fq; +use crate::fields::fp::FpGadget; + +pub type FqGadget = FpGadget; diff --git a/r1cs-std/src/fields/edwards_sw6.rs b/r1cs-std/src/fields/edwards_sw6.rs new file mode 100644 index 0000000..9c4969c --- /dev/null +++ b/r1cs-std/src/fields/edwards_sw6.rs @@ -0,0 +1,4 @@ +use algebra::fields::edwards_sw6::fq::Fq; +use crate::fields::fp::FpGadget; + +pub type FqGadget = FpGadget; diff --git a/r1cs-std/src/fields/fp.rs b/r1cs-std/src/fields/fp.rs new file mode 100644 index 0000000..b78204a --- /dev/null +++ b/r1cs-std/src/fields/fp.rs @@ -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 { + pub value: Option, + pub variable: ConstraintVar, +} + +impl FpGadget { + #[inline] + pub fn from>(mut cs: CS, value: &F) -> Self { + Self::alloc(cs.ns(|| "from"), || Ok(*value)).unwrap() + } +} + +impl FieldGadget for FpGadget { + type Variable = ConstraintVar; + + #[inline] + fn get_value(&self) -> Option { + self.value + } + + #[inline] + fn get_variable(&self) -> Self::Variable { + self.variable.clone() + } + + #[inline] + fn zero>(_cs: CS) -> Result { + let value = Some(F::zero()); + Ok(FpGadget { + value, + variable: ConstraintVar::zero(), + }) + } + + #[inline] + fn one>(_cs: CS) -> Result { + let value = Some(F::one()); + Ok(FpGadget { + value, + variable: CS::one().into(), + }) + } + + #[inline] + fn add>( + &self, + mut _cs: CS, + other: &Self, + ) -> Result { + 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>(&self, _cs: CS) -> Result { + 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>( + &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>( + &self, + mut _cs: CS, + other: &Self, + ) -> Result { + 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>(&self, cs: CS) -> Result { + let mut result = self.clone(); + result.negate_in_place(cs)?; + Ok(result) + } + + #[inline] + fn negate_in_place>( + &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>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + 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>( + &self, + _cs: CS, + other: &F, + ) -> Result { + let value = self.value.map(|val| val + other); + Ok(FpGadget { + value, + variable: self.variable.clone() + (*other, CS::one()), + }) + } + + #[inline] + fn add_constant_in_place>( + &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>( + &self, + cs: CS, + other: &F, + ) -> Result { + let mut result = self.clone(); + result.mul_by_constant_in_place(cs, other)?; + Ok(result) + } + + #[inline] + fn mul_by_constant_in_place>( + &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>(&self, mut cs: CS) -> Result { + 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>( + &self, + _: CS, + _: usize, + ) -> Result { + Ok(self.clone()) + } + + fn frobenius_map_in_place>( + &mut self, + _: CS, + _: usize, + ) -> Result<&mut Self, SynthesisError> { + Ok(self) + } + + fn mul_equals>( + &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>( + &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 PartialEq for FpGadget { + fn eq(&self, other: &Self) -> bool { + !self.value.is_none() && !other.value.is_none() && self.value == other.value + } +} + +impl Eq for FpGadget {} + +impl EqGadget for FpGadget {} + +impl ConditionalEqGadget for FpGadget { + #[inline] + fn conditional_enforce_equal>( + &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 NEqGadget for FpGadget { + #[inline] + fn enforce_not_equal>( + &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 ToBitsGadget for FpGadget { + /// Outputs the binary representation of the value in `self` in *big-endian* + /// form. + fn to_bits>(&self, mut cs: CS) -> Result, 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>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + let bits = self.to_bits(&mut cs)?; + Boolean::enforce_in_field::<_, _, F>(&mut cs, &bits)?; + + Ok(bits) + } +} + +impl ToBytesGadget for FpGadget { + fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + let byte_values = match self.value { + Some(value) => to_bytes![&value.into_repr()]? + .into_iter() + .map(Some) + .collect::>(), + 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>( + &self, + mut cs: CS, + ) -> Result, 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::>(), + )?; + + Ok(bytes) + } +} + +impl CondSelectGadget for FpGadget { + #[inline] + fn conditionally_select>( + mut cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + 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 TwoBitLookupGadget for FpGadget { + type TableConstant = F; + fn two_bit_lookup>( + mut cs: CS, + b: &[Boolean], + c: &[Self::TableConstant], + ) -> Result { + 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 Clone for FpGadget { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + variable: self.variable.clone(), + } + } +} + +impl AllocGadget for FpGadget { + #[inline] + fn alloc>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow, + { + 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>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow, + { + 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), + }) + } +} diff --git a/r1cs-std/src/fields/fp12.rs b/r1cs-std/src/fields/fp12.rs new file mode 100644 index 0000000..e05b8c6 --- /dev/null +++ b/r1cs-std/src/fields/fp12.rs @@ -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 = + super::fp2::Fp2Gadget<<

::Fp6Params as Fp6Parameters>::Fp2Params, ConstraintF>; +type Fp6Gadget = super::fp6_3over2::Fp6Gadget<

::Fp6Params, ConstraintF>; +type Fp6GadgetVariable = + as FieldGadget::Fp6Params>, ConstraintF>>::Variable; + +#[derive(Derivative)] +#[derivative(Debug(bound = "ConstraintF: PrimeField"))] +#[must_use] +pub struct Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + pub c0: Fp6Gadget, + pub c1: Fp6Gadget, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +impl Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + #[inline] + pub fn new(c0: Fp6Gadget, c1: Fp6Gadget) -> Self { + Self { + c0, + c1, + _params: PhantomData, + } + } + + /// Multiply by quadratic nonresidue v. + #[inline] + pub(crate) fn mul_fp6_by_nonresidue>( + cs: CS, + fe: &Fp6Gadget, + ) -> Result, SynthesisError> { + let new_c0 = Fp6Gadget::::mul_fp2_gadget_by_nonresidue(cs, &fe.c2)?; + let new_c1 = fe.c0.clone(); + let new_c2 = fe.c1.clone(); + Ok(Fp6Gadget::::new(new_c0, new_c1, new_c2)) + } + + #[inline] + pub fn conjugate_in_place>( + &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>( + &self, + mut cs: CS, + c0: &Fp2Gadget, + c1: &Fp2Gadget, + d1: &Fp2Gadget, + ) -> Result { + 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>( + &self, + mut cs: CS, + c0: &Fp2Gadget, + d0: &Fp2Gadget, + d1: &Fp2Gadget, + ) -> Result { + 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::::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>( + &self, + mut cs: CS, + ) -> Result { + let mut result = Self::zero(cs.ns(|| "alloc result"))?; + let fp2_nr = ::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, S: AsRef<[u64]>>( + &self, + mut cs: CS, + exp: S, + ) -> Result { + 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 FieldGadget, ConstraintF> for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + type Variable = (Fp6GadgetVariable, Fp6GadgetVariable); + + #[inline] + fn get_value(&self) -> Option> { + 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>(mut cs: CS) -> Result { + let c0 = Fp6Gadget::::zero(cs.ns(|| "c0"))?; + let c1 = Fp6Gadget::::zero(cs.ns(|| "c1"))?; + Ok(Self::new(c0, c1)) + } + + #[inline] + fn one>(mut cs: CS) -> Result { + let c0 = Fp6Gadget::::one(cs.ns(|| "c0"))?; + let c1 = Fp6Gadget::::zero(cs.ns(|| "c1"))?; + Ok(Self::new(c0, c1)) + } + + #[inline] + fn add>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + 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>( + &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>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + 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>( + &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>(&self, mut cs: CS) -> Result { + 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>( + &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>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + // 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>(&self, mut cs: CS) -> Result { + // 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>( + &self, + mut cs: CS, + other: &Fp12

, + ) -> Result { + 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>( + &mut self, + mut cs: CS, + other: &Fp12

, + ) -> 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>( + &self, + mut cs: CS, + other: &Fp12

, + ) -> Result { + // 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>( + &self, + cs: CS, + power: usize, + ) -> Result { + let mut res = self.clone(); + res.frobenius_map_in_place(cs, power)?; + Ok(res) + } + + fn frobenius_map_in_place>( + &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>(&self, mut cs: CS) -> Result { + 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::::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>( + &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() + >::cost() + } +} + +impl PartialEq for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + fn eq(&self, other: &Self) -> bool { + self.c0 == other.c0 && self.c1 == other.c1 + } +} + +impl Eq for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ +} + +impl EqGadget for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ +} + +impl ConditionalEqGadget for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + #[inline] + fn conditional_enforce_equal>( + &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 * as ConditionalEqGadget>::cost() + } +} + +impl NEqGadget for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + #[inline] + fn enforce_not_equal>( + &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 * as NEqGadget>::cost() + } +} + +impl ToBitsGadget for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + fn to_bits>(&self, mut cs: CS) -> Result, 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>( + &self, + mut cs: CS, + ) -> Result, 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 ToBytesGadget for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + fn to_bytes>(&self, mut cs: CS) -> Result, 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>( + &self, + mut cs: CS, + ) -> Result, 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 Clone for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + fn clone(&self) -> Self { + Self::new(self.c0.clone(), self.c1.clone()) + } +} + +impl CondSelectGadget for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + #[inline] + fn conditionally_select>( + mut cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + let c0 = Fp6Gadget::::conditionally_select( + &mut cs.ns(|| "c0"), + cond, + &first.c0, + &second.c0, + )?; + let c1 = Fp6Gadget::::conditionally_select( + &mut cs.ns(|| "c1"), + cond, + &first.c1, + &second.c1, + )?; + + Ok(Self::new(c0, c1)) + } + + fn cost() -> usize { + 2 * as CondSelectGadget>::cost() + } +} + +impl TwoBitLookupGadget for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + type TableConstant = Fp12

; + fn two_bit_lookup>( + mut cs: CS, + b: &[Boolean], + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c0 = Fp6Gadget::::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; + let c1 = Fp6Gadget::::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; + Ok(Self::new(c0, c1)) + } + + fn cost() -> usize { + 2 * as TwoBitLookupGadget>::cost() + } +} + +impl AllocGadget, ConstraintF> for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + #[inline] + fn alloc>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow>, + { + 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::::alloc(&mut cs.ns(|| "c0"), || c0)?; + let c1 = Fp6Gadget::::alloc(&mut cs.ns(|| "c1"), || c1)?; + Ok(Self::new(c0, c1)) + } + + #[inline] + fn alloc_input>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow>, + { + 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::::alloc_input(&mut cs.ns(|| "c0"), || c0)?; + let c1 = Fp6Gadget::::alloc_input(&mut cs.ns(|| "c1"), || c1)?; + Ok(Self::new(c0, c1)) + } +} diff --git a/r1cs-std/src/fields/fp2.rs b/r1cs-std/src/fields/fp2.rs new file mode 100644 index 0000000..ebf2ec9 --- /dev/null +++ b/r1cs-std/src/fields/fp2.rs @@ -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, ConstraintF: PrimeField> { + pub c0: FpGadget, + pub c1: FpGadget, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +impl, ConstraintF: PrimeField> Fp2Gadget { + pub fn new(c0: FpGadget, c1: FpGadget) -> Self { + Self { + c0, + c1, + _params: PhantomData, + } + } + + /// Multiply a FpGadget by quadratic nonresidue P::NONRESIDUE. + #[inline] + pub fn mul_fp_gadget_by_nonresidue>( + cs: CS, + fe: &FpGadget, + ) -> Result, 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>( + &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>( + &self, + cs: CS, + fe: &P::Fp, + ) -> Result { + let mut result = self.clone(); + result.mul_by_fp_constant_in_place(cs, fe)?; + Ok(result) + } +} + +impl, ConstraintF: PrimeField> FieldGadget, ConstraintF> for Fp2Gadget { + type Variable = (ConstraintVar, ConstraintVar); + + #[inline] + fn get_value(&self) -> Option> { + 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>(mut cs: CS) -> Result { + let c0 = FpGadget::zero(cs.ns(|| "c0"))?; + let c1 = FpGadget::zero(cs.ns(|| "c1"))?; + Ok(Self::new(c0, c1)) + } + + #[inline] + fn one>(mut cs: CS) -> Result { + let c0 = FpGadget::one(cs.ns(|| "c0"))?; + let c1 = FpGadget::zero(cs.ns(|| "c1"))?; + Ok(Self::new(c0, c1)) + } + + #[inline] + fn add>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + 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>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + 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>(&self, cs: CS) -> Result { + let mut result = self.clone(); + result.double_in_place(cs)?; + Ok(result) + } + + #[inline] + fn double_in_place>( + &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>(&self, cs: CS) -> Result { + let mut result = self.clone(); + result.negate_in_place(cs)?; + Ok(result) + } + + #[inline] + fn negate_in_place>( + &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>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + // 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>(&self, mut cs: CS) -> Result { + // 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>( + &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>(&self, mut cs: CS) -> Result { + 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>( + &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>( + &self, + cs: CS, + power: usize, + ) -> Result { + let mut result = self.clone(); + let _ = result.frobenius_map_in_place(cs, power)?; + Ok(result) + } + + fn frobenius_map_in_place>( + &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>( + &self, + cs: CS, + other: &Fp2

, + ) -> Result { + let mut result = self.clone(); + let _ = result.add_constant_in_place(cs, other)?; + Ok(result) + } + + #[inline] + fn add_constant_in_place>( + &mut self, + mut cs: CS, + other: &Fp2

, + ) -> 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>( + &self, + mut cs: CS, + fe: &Fp2

, + ) -> Result { + // 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, ConstraintF: PrimeField> PartialEq for Fp2Gadget { + fn eq(&self, other: &Self) -> bool { + self.c0 == other.c0 && self.c1 == other.c1 + } +} + +impl, ConstraintF: PrimeField> Eq for Fp2Gadget {} + +impl, ConstraintF: PrimeField> EqGadget for Fp2Gadget {} + +impl, ConstraintF: PrimeField> ConditionalEqGadget for Fp2Gadget { + #[inline] + fn conditional_enforce_equal>( + &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, ConstraintF: PrimeField> NEqGadget for Fp2Gadget { + #[inline] + fn enforce_not_equal>( + &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, ConstraintF: PrimeField> ToBitsGadget for Fp2Gadget { + fn to_bits>(&self, mut cs: CS) -> Result, 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>( + &self, + mut cs: CS, + ) -> Result, 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, ConstraintF: PrimeField> ToBytesGadget for Fp2Gadget { + fn to_bytes>(&self, mut cs: CS) -> Result, 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>( + &self, + mut cs: CS, + ) -> Result, 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, ConstraintF: PrimeField> Clone for Fp2Gadget { + fn clone(&self) -> Self { + Self { + c0: self.c0.clone(), + c1: self.c1.clone(), + _params: PhantomData, + } + } +} + +impl, ConstraintF: PrimeField> CondSelectGadget for Fp2Gadget { + #[inline] + fn conditionally_select>( + mut cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + let c0 = + FpGadget::::conditionally_select(&mut cs.ns(|| "c0"), cond, &first.c0, &second.c0)?; + let c1 = + FpGadget::::conditionally_select(&mut cs.ns(|| "c1"), cond, &first.c1, &second.c1)?; + + Ok(Self::new(c0, c1)) + } + + fn cost() -> usize { + 2 + } +} + +impl, ConstraintF: PrimeField> TwoBitLookupGadget for Fp2Gadget { + type TableConstant = Fp2

; + fn two_bit_lookup>( + mut cs: CS, + b: &[Boolean], + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + 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 * as TwoBitLookupGadget>::cost() + } +} + +impl, ConstraintF: PrimeField> AllocGadget, ConstraintF> for Fp2Gadget { + #[inline] + fn alloc>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow>, + { + 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>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow>, + { + 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)) + } +} diff --git a/r1cs-std/src/fields/fp6_3over2.rs b/r1cs-std/src/fields/fp6_3over2.rs new file mode 100644 index 0000000..22c15cb --- /dev/null +++ b/r1cs-std/src/fields/fp6_3over2.rs @@ -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 = super::fp2::Fp2Gadget<

::Fp2Params, ConstraintF>; + +#[derive(Derivative)] +#[derivative(Debug(bound = "ConstraintF: PrimeField"))] +#[must_use] +pub struct Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + pub c0: Fp2Gadget, + pub c1: Fp2Gadget, + pub c2: Fp2Gadget, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +impl Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + #[inline] + pub fn new(c0: Fp2Gadget, c1: Fp2Gadget, c2: Fp2Gadget) -> Self { + Self { + c0, + c1, + c2, + _params: PhantomData, + } + } + /// Multiply a Fp2Gadget by cubic nonresidue P::NONRESIDUE. + #[inline] + pub fn mul_fp2_gadget_by_nonresidue>( + cs: CS, + fe: &Fp2Gadget, + ) -> Result, SynthesisError> { + fe.mul_by_constant(cs, &P::NONRESIDUE) + } + + #[inline] + pub fn mul_by_0_c1_0>( + &self, + mut cs: CS, + c1: &Fp2Gadget, + ) -> Result { + // 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>( + &self, + mut cs: CS, + c0: &Fp2Gadget, + c1: &Fp2Gadget, + ) -> Result { + 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 FieldGadget, ConstraintF> for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + type Variable = ( + (ConstraintVar, ConstraintVar), + (ConstraintVar, ConstraintVar), + (ConstraintVar, ConstraintVar), + ); + + #[inline] + fn get_value(&self) -> Option> { + 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>(mut cs: CS) -> Result { + let c0 = Fp2Gadget::::zero(cs.ns(|| "c0"))?; + let c1 = Fp2Gadget::::zero(cs.ns(|| "c1"))?; + let c2 = Fp2Gadget::::zero(cs.ns(|| "c2"))?; + Ok(Self::new(c0, c1, c2)) + } + + #[inline] + fn one>(mut cs: CS) -> Result { + let c0 = Fp2Gadget::::one(cs.ns(|| "c0"))?; + let c1 = Fp2Gadget::::zero(cs.ns(|| "c1"))?; + let c2 = Fp2Gadget::::zero(cs.ns(|| "c2"))?; + Ok(Self::new(c0, c1, c2)) + } + + #[inline] + fn add>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + 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>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + 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>(&self, mut cs: CS) -> Result { + 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>( + &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>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + // 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 = ::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>(&self, mut cs: CS) -> Result { + // 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 = ::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>(&self, mut cs: CS) -> Result { + 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>( + &self, + mut cs: CS, + other: &Fp6

, + ) -> Result { + 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>( + &mut self, + mut cs: CS, + other: &Fp6

, + ) -> 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>( + &self, + mut cs: CS, + other: &Fp6

, + ) -> Result { + // 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 = ::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>( + &self, + cs: CS, + power: usize, + ) -> Result { + let mut result = self.clone(); + result.frobenius_map_in_place(cs, power)?; + Ok(result) + } + + fn frobenius_map_in_place>( + &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::::cost_of_mul() + } + + fn cost_of_inv() -> usize { + Self::cost_of_mul() + >::cost() + } +} + +impl PartialEq for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + fn eq(&self, other: &Self) -> bool { + self.c0 == other.c0 && self.c1 == other.c1 && self.c2 == other.c2 + } +} + +impl Eq for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ +} + +impl EqGadget for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ +} + +impl ConditionalEqGadget for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + #[inline] + fn conditional_enforce_equal>( + &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 * as ConditionalEqGadget>::cost() + } +} + +impl NEqGadget for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + #[inline] + fn enforce_not_equal>( + &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 * as NEqGadget>::cost() + } +} + +impl ToBitsGadget for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + fn to_bits>(&self, mut cs: CS) -> Result, 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>( + &self, + mut cs: CS, + ) -> Result, 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 ToBytesGadget for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + fn to_bytes>(&self, mut cs: CS) -> Result, 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>( + &self, + cs: CS, + ) -> Result, SynthesisError> { + self.to_bytes(cs) + } +} + +impl Clone for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + fn clone(&self) -> Self { + Self::new(self.c0.clone(), self.c1.clone(), self.c2.clone()) + } +} + +impl CondSelectGadget for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + #[inline] + fn conditionally_select>( + mut cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + let c0 = Fp2Gadget::::conditionally_select( + &mut cs.ns(|| "c0"), + cond, + &first.c0, + &second.c0, + )?; + let c1 = Fp2Gadget::::conditionally_select( + &mut cs.ns(|| "c1"), + cond, + &first.c1, + &second.c1, + )?; + let c2 = Fp2Gadget::::conditionally_select( + &mut cs.ns(|| "c2"), + cond, + &first.c2, + &second.c2, + )?; + + Ok(Self::new(c0, c1, c2)) + } + + fn cost() -> usize { + 3 * as CondSelectGadget>::cost() + } +} + +impl TwoBitLookupGadget for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + type TableConstant = Fp6

; + fn two_bit_lookup>( + mut cs: CS, + b: &[Boolean], + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c2s = c.iter().map(|f| f.c2).collect::>(); + let c0 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; + let c1 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; + let c2 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c2"), b, &c2s)?; + Ok(Self::new(c0, c1, c2)) + } + + fn cost() -> usize { + 3 * as TwoBitLookupGadget>::cost() + } +} + +impl AllocGadget, ConstraintF> for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + #[inline] + fn alloc>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow>, + { + 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::::alloc(&mut cs.ns(|| "c0"), || c0)?; + let c1 = Fp2Gadget::::alloc(&mut cs.ns(|| "c1"), || c1)?; + let c2 = Fp2Gadget::::alloc(&mut cs.ns(|| "c2"), || c2)?; + Ok(Self::new(c0, c1, c2)) + } + + #[inline] + fn alloc_input>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow>, + { + 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::::alloc_input(&mut cs.ns(|| "c0"), || c0)?; + let c1 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c1"), || c1)?; + let c2 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c2"), || c2)?; + Ok(Self::new(c0, c1, c2)) + } +} diff --git a/r1cs-std/src/fields/jubjub.rs b/r1cs-std/src/fields/jubjub.rs new file mode 100644 index 0000000..7a15e1c --- /dev/null +++ b/r1cs-std/src/fields/jubjub.rs @@ -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; diff --git a/r1cs-std/src/fields/mod.rs b/r1cs-std/src/fields/mod.rs new file mode 100644 index 0000000..e3aeebc --- /dev/null +++ b/r1cs-std/src/fields/mod.rs @@ -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: + Sized + + Clone + + EqGadget + + NEqGadget + + ConditionalEqGadget + + ToBitsGadget + + AllocGadget + + ToBytesGadget + + CondSelectGadget + + TwoBitLookupGadget + + Debug +{ + type Variable: Clone + Debug; + + fn get_value(&self) -> Option; + + fn get_variable(&self) -> Self::Variable; + + fn zero>(_: CS) -> Result; + + fn one>(_: CS) -> Result; + + fn add>(&self, _: CS, _: &Self) -> Result; + + fn add_in_place>( + &mut self, + cs: CS, + other: &Self, + ) -> Result<&mut Self, SynthesisError> { + *self = self.add(cs, other)?; + Ok(self) + } + + fn double>(&self, cs: CS) -> Result { + self.add(cs, &self) + } + + fn double_in_place>( + &mut self, + cs: CS, + ) -> Result<&mut Self, SynthesisError> { + *self = self.double(cs)?; + Ok(self) + } + + fn sub>(&self, _: CS, _: &Self) -> Result; + + fn sub_in_place>( + &mut self, + cs: CS, + other: &Self, + ) -> Result<&mut Self, SynthesisError> { + *self = self.sub(cs, other)?; + Ok(self) + } + + fn negate>(&self, _: CS) -> Result; + + #[inline] + fn negate_in_place>( + &mut self, + cs: CS, + ) -> Result<&mut Self, SynthesisError> { + *self = self.negate(cs)?; + Ok(self) + } + + fn mul>(&self, _: CS, _: &Self) -> Result; + + fn mul_in_place>( + &mut self, + cs: CS, + other: &Self, + ) -> Result<&mut Self, SynthesisError> { + *self = self.mul(cs, other)?; + Ok(self) + } + + fn square>(&self, cs: CS) -> Result { + self.mul(cs, &self) + } + + fn square_in_place>( + &mut self, + cs: CS, + ) -> Result<&mut Self, SynthesisError> { + *self = self.square(cs)?; + Ok(self) + } + + fn mul_equals>( + &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>( + &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>(&self, _: CS, _: &F) -> Result; + + fn add_constant_in_place>( + &mut self, + cs: CS, + other: &F, + ) -> Result<&mut Self, SynthesisError> { + *self = self.add_constant(cs, other)?; + Ok(self) + } + + fn sub_constant>( + &self, + cs: CS, + fe: &F, + ) -> Result { + self.add_constant(cs, &(-(*fe))) + } + + fn sub_constant_in_place>( + &mut self, + cs: CS, + other: &F, + ) -> Result<&mut Self, SynthesisError> { + self.add_constant_in_place(cs, &(-(*other))) + } + + fn mul_by_constant>( + &self, + _: CS, + _: &F, + ) -> Result; + + fn mul_by_constant_in_place>( + &mut self, + cs: CS, + other: &F, + ) -> Result<&mut Self, SynthesisError> { + *self = self.mul_by_constant(cs, other)?; + Ok(self) + } + + fn inverse>(&self, _: CS) -> Result; + + fn frobenius_map>( + &self, + _: CS, + power: usize, + ) -> Result; + + fn frobenius_map_in_place>( + &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>( + &self, + mut cs: CS, + bits: &[Boolean], + ) -> Result { + 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, CS: ConstraintSystem>( + 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::>(); + 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, + CS: ConstraintSystem, + >( + 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::::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::(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::(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::(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::::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::::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()); + } +} diff --git a/r1cs-std/src/groups/curves/mod.rs b/r1cs-std/src/groups/curves/mod.rs new file mode 100644 index 0000000..11cea99 --- /dev/null +++ b/r1cs-std/src/groups/curves/mod.rs @@ -0,0 +1,2 @@ +pub mod short_weierstrass; +pub mod twisted_edwards; diff --git a/r1cs-std/src/groups/curves/short_weierstrass/bls12/bls12_377.rs b/r1cs-std/src/groups/curves/short_weierstrass/bls12/bls12_377.rs new file mode 100644 index 0000000..7f379a1 --- /dev/null +++ b/r1cs-std/src/groups/curves/short_weierstrass/bls12/bls12_377.rs @@ -0,0 +1,213 @@ +use crate::groups::bls12::{ + G1Gadget as Bls12G1Gadget, G1PreparedGadget as Bls12G1PreparedGadget, + G2Gadget as Bls12G2Gadget, G2PreparedGadget as Bls12G2PreparedGadget, +}; +use algebra::curves::bls12_377::Bls12_377Parameters; + +pub type G1Gadget = Bls12G1Gadget; +pub type G2Gadget = Bls12G2Gadget; + +pub type G1PreparedGadget = Bls12G1PreparedGadget; +pub type G2PreparedGadget = Bls12G2PreparedGadget; + +#[cfg(test)] +mod test { + use crate::fields::FieldGadget; + use rand; + + use super::{G1Gadget, G2Gadget}; + use crate::{ + boolean::Boolean, + groups::GroupGadget, + test_constraint_system::TestConstraintSystem, + utils::{AllocGadget, CondSelectGadget, EqGadget}, + }; + use algebra::{ + curves::bls12_377::{G1Projective as G1, G2Projective as G2}, + fields::bls12_377::Fr, + fields::bls12_377::Fq, + AffineCurve, BitIterator, PrimeField, ProjectiveCurve, + }; + use r1cs_core::ConstraintSystem; + + #[test] + fn bls12_g1_constraint_costs() { + use crate::boolean::AllocatedBit; + + let mut cs = TestConstraintSystem::::new(); + + let bit = AllocatedBit::alloc(&mut cs.ns(|| "bool"), || Ok(true)) + .unwrap() + .into(); + + let a: G1 = rand::random(); + let b: G1 = rand::random(); + let gadget_a = G1Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); + let gadget_b = G1Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); + let alloc_cost = cs.num_constraints(); + let _ = G1Gadget::conditionally_select( + &mut cs.ns(|| "cond_select"), + &bit, + &gadget_a, + &gadget_b, + ) + .unwrap(); + let cond_select_cost = cs.num_constraints() - alloc_cost; + + let _ = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap(); + let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost; + + assert!(cs.is_satisfied()); + assert_eq!( + cond_select_cost, + >::cost() + ); + assert_eq!(add_cost, G1Gadget::cost_of_add()); + } + + #[test] + fn bls12_g2_constraint_costs() { + use crate::boolean::AllocatedBit; + + let mut cs = TestConstraintSystem::::new(); + + let bit = AllocatedBit::alloc(&mut cs.ns(|| "bool"), || Ok(true)) + .unwrap() + .into(); + + let a: G2 = rand::random(); + let b: G2 = rand::random(); + let gadget_a = G2Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); + let gadget_b = G2Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); + let alloc_cost = cs.num_constraints(); + let _ = G2Gadget::conditionally_select( + &mut cs.ns(|| "cond_select"), + &bit, + &gadget_a, + &gadget_b, + ) + .unwrap(); + let cond_select_cost = cs.num_constraints() - alloc_cost; + + let _ = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap(); + let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost; + + assert!(cs.is_satisfied()); + assert_eq!( + cond_select_cost, + >::cost() + ); + assert_eq!(add_cost, G2Gadget::cost_of_add()); + } + + #[test] + fn bls12_g1_gadget_test() { + use rand::{Rand, SeedableRng, XorShiftRng}; + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut cs = TestConstraintSystem::::new(); + + let a = G1::rand(&mut rng); + let b = G1::rand(&mut rng); + let a_affine = a.into_affine(); + let b_affine = b.into_affine(); + let mut gadget_a = G1Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); + let gadget_b = G1Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); + assert_eq!(gadget_a.x.value.unwrap(), a_affine.x); + assert_eq!(gadget_a.y.value.unwrap(), a_affine.y); + assert_eq!(gadget_b.x.value.unwrap(), b_affine.x); + assert_eq!(gadget_b.y.value.unwrap(), b_affine.y); + + // Check addition + let ab = a + &b; + let ab_affine = ab.into_affine(); + let gadget_ab = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap(); + let gadget_ba = gadget_b.add(&mut cs.ns(|| "ba"), &gadget_a).unwrap(); + gadget_ba + .enforce_equal(&mut cs.ns(|| "b + a == a + b?"), &gadget_ab) + .unwrap(); + + let ab_val = gadget_ab + .get_value() + .expect("Doubling should be successful") + .into_affine(); + assert_eq!(ab_val, ab_affine, "Result of addition is unequal"); + + // Check doubling + let aa = a.double(); + let aa_affine = aa.into_affine(); + gadget_a.double_in_place(&mut cs.ns(|| "2a")).unwrap(); + let aa_val = gadget_a + .get_value() + .expect("Doubling should be successful") + .into_affine(); + assert_eq!( + aa_val, aa_affine, + "Gadget and native values are unequal after double." + ); + + // Check mul_bits + let scalar = Fr::rand(&mut rng); + let native_result = aa.into_affine().mul(scalar) + &b; + let native_result = native_result.into_affine(); + + let mut scalar: Vec = BitIterator::new(scalar.into_repr()).collect(); + // Get the scalar bits into little-endian form. + scalar.reverse(); + let input = Vec::::alloc(cs.ns(|| "Input"), || Ok(scalar)).unwrap(); + let result = gadget_a + .mul_bits(cs.ns(|| "mul_bits"), &gadget_b, input.iter()) + .unwrap(); + let result_val = result.get_value().unwrap().into_affine(); + assert_eq!( + result_val, native_result, + "gadget & native values are diff. after scalar mul" + ); + + if !cs.is_satisfied() { + println!("{:?}", cs.which_is_unsatisfied().unwrap()); + } + + assert!(cs.is_satisfied()); + } + + #[test] + fn bls12_g2_gadget_test() { + let mut cs = TestConstraintSystem::::new(); + + let a: G2 = rand::random(); + let b: G2 = rand::random(); + let a_affine = a.into_affine(); + let b_affine = b.into_affine(); + + let mut gadget_a = G2Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); + let gadget_b = G2Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); + assert_eq!(gadget_a.x.get_value().unwrap(), a_affine.x); + assert_eq!(gadget_a.y.get_value().unwrap(), a_affine.y); + assert_eq!(gadget_b.x.get_value().unwrap(), b_affine.x); + assert_eq!(gadget_b.y.get_value().unwrap(), b_affine.y); + + let ab = a + &b; + let ab_affine = ab.into_affine(); + let gadget_ab = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap(); + let gadget_ba = gadget_b.add(&mut cs.ns(|| "ba"), &gadget_a).unwrap(); + gadget_ba + .enforce_equal(&mut cs.ns(|| "b + a == a + b?"), &gadget_ab) + .unwrap(); + assert_eq!(gadget_ab.x.get_value().unwrap(), ab_affine.x); + assert_eq!(gadget_ab.y.get_value().unwrap(), ab_affine.y); + + let aa = a.double(); + let aa_affine = aa.into_affine(); + gadget_a.double_in_place(&mut cs.ns(|| "2a")).unwrap(); + + assert_eq!(gadget_a.x.get_value().unwrap(), aa_affine.x); + assert_eq!(gadget_a.y.get_value().unwrap(), aa_affine.y); + + if !cs.is_satisfied() { + println!("{:?}", cs.which_is_unsatisfied().unwrap()); + } + + assert!(cs.is_satisfied()); + } +} diff --git a/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs new file mode 100644 index 0000000..7cc4bc8 --- /dev/null +++ b/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs @@ -0,0 +1,168 @@ +use algebra::{ + curves::bls12::{Bls12Parameters, G1Prepared, TwistType}, + fields::Field, + BitIterator, ProjectiveCurve, +}; +use r1cs_core::{ConstraintSystem, SynthesisError}; + + +use crate::prelude::*; +use crate::fields::{fp::FpGadget, fp2::Fp2Gadget, FieldGadget}; +use crate::groups::curves::short_weierstrass::AffineGadget; + +use std::fmt::Debug; + +pub mod bls12_377; + +pub type G1Gadget

= AffineGadget<

::G1Parameters,

::Fp, FpGadget<

::Fp>>; +pub type G2Gadget

= AffineGadget<

::G2Parameters,

::Fp, Fp2G

>; + +#[derive(Derivative)] +#[derivative( + Clone(bound = "G1Gadget

: Clone"), + Debug(bound = "G1Gadget

: Debug") +)] +pub struct G1PreparedGadget(pub G1Gadget

); + +impl G1PreparedGadget

{ + pub fn get_value(&self) -> Option> { + Some(G1Prepared::from_affine( + self.0.get_value().unwrap().into_affine(), + )) + } + + pub fn from_affine>( + _cs: CS, + q: &G1Gadget

, + ) -> Result { + Ok(G1PreparedGadget(q.clone())) + } +} + +impl ToBytesGadget for G1PreparedGadget

{ + #[inline] + fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + self.0.to_bytes(&mut cs.ns(|| "g_alpha to bytes")) + } + + fn to_bytes_strict>( + &self, + cs: CS, + ) -> Result, SynthesisError> { + self.to_bytes(cs) + } +} + +type Fp2G

= Fp2Gadget<

::Fp2Params,

::Fp>; +type LCoeff

= (Fp2G

, Fp2G

); +#[derive(Derivative)] +#[derivative( + Clone(bound = "Fp2Gadget: Clone"), + Debug(bound = "Fp2Gadget: Debug") +)] +pub struct G2PreparedGadget { + pub ell_coeffs: Vec>, +} + +impl ToBytesGadget for G2PreparedGadget

{ + #[inline] + fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + let mut bytes = Vec::new(); + for (i, coeffs) in self.ell_coeffs.iter().enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + bytes.extend_from_slice(&coeffs.0.to_bytes(&mut cs.ns(|| "c0"))?); + bytes.extend_from_slice(&coeffs.1.to_bytes(&mut cs.ns(|| "c1"))?); + } + Ok(bytes) + } + + fn to_bytes_strict>( + &self, + cs: CS, + ) -> Result, SynthesisError> { + self.to_bytes(cs) + } +} + +impl G2PreparedGadget

{ + pub fn from_affine>( + mut cs: CS, + q: &G2Gadget

, + ) -> Result { + let two_inv = P::Fp::one().double().inverse().unwrap(); + let zero = G2Gadget::

::zero(cs.ns(|| "zero"))?; + q.enforce_not_equal(cs.ns(|| "enforce not zero"), &zero)?; + let mut ell_coeffs = vec![]; + let mut r = q.clone(); + + for (j, i) in BitIterator::new(P::X).skip(1).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", j)); + ell_coeffs.push(Self::double(cs.ns(|| "double"), &mut r, &two_inv)?); + + if i { + ell_coeffs.push(Self::add(cs.ns(|| "add"), &mut r, &q)?); + } + } + + Ok(Self { ell_coeffs }) + } + + fn double>( + mut cs: CS, + r: &mut G2Gadget

, + two_inv: &P::Fp, + ) -> Result, SynthesisError> { + let a = r.y.inverse(cs.ns(|| "Inverse"))?; + let mut b = r.x.square(cs.ns(|| "square x"))?; + let b_tmp = b.clone(); + b.mul_by_fp_constant_in_place(cs.ns(|| "mul by two_inv"), two_inv)?; + b.add_in_place(cs.ns(|| "compute b"), &b_tmp)?; + + let c = a.mul(cs.ns(|| "compute c"), &b)?; + let d = r.x.double(cs.ns(|| "compute d"))?; + let x3 = c.square(cs.ns(|| "c^2"))?.sub(cs.ns(|| "sub d"), &d)?; + let e = c + .mul(cs.ns(|| "c*r.x"), &r.x)? + .sub(cs.ns(|| "sub r.y"), &r.y)?; + let c_x3 = c.mul(cs.ns(|| "c*x_3"), &x3)?; + let y3 = e.sub(cs.ns(|| "e = c * x3"), &c_x3)?; + let mut f = c; + f.negate_in_place(cs.ns(|| "c = -c"))?; + r.x = x3; + r.y = y3; + match P::TWIST_TYPE { + TwistType::M => Ok((e, f)), + TwistType::D => Ok((f, e)), + } + } + + fn add>( + mut cs: CS, + r: &mut G2Gadget

, + q: &G2Gadget

, + ) -> Result, SynthesisError> { + let a = + q.x.sub(cs.ns(|| "q.x - r.x"), &r.x)? + .inverse(cs.ns(|| "calc a"))?; + let b = q.y.sub(cs.ns(|| "q.y - r.y"), &r.y)?; + let c = a.mul(cs.ns(|| "compute c"), &b)?; + let d = r.x.add(cs.ns(|| "r.x + q.x"), &q.x)?; + let x3 = c.square(cs.ns(|| "c^2"))?.sub(cs.ns(|| "sub d"), &d)?; + + let e = + r.x.sub(cs.ns(|| "r.x - x3"), &x3)? + .mul(cs.ns(|| "c * (r.x - x3)"), &c)?; + let y3 = e.sub(cs.ns(|| "calc y3"), &r.y)?; + let g = c + .mul(cs.ns(|| "c*r.x"), &r.x)? + .sub(cs.ns(|| "calc g"), &r.y)?; + let mut f = c; + f.negate_in_place(cs.ns(|| "c = -c"))?; + r.x = x3; + r.y = y3; + match P::TWIST_TYPE { + TwistType::M => Ok((g, f)), + TwistType::D => Ok((f, g)), + } + } +} diff --git a/r1cs-std/src/groups/curves/short_weierstrass/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/mod.rs new file mode 100644 index 0000000..7a84cbc --- /dev/null +++ b/r1cs-std/src/groups/curves/short_weierstrass/mod.rs @@ -0,0 +1,625 @@ +use algebra::{AffineCurve, Field, ProjectiveCurve}; +use algebra::{ + curves::{ + short_weierstrass_jacobian::{GroupAffine as SWAffine, GroupProjective as SWProjective}, + SWModelParameters, + }, + BitIterator, PrimeField, +}; +use r1cs_core::{ConstraintSystem, SynthesisError}; +use std::{borrow::Borrow, marker::PhantomData, ops::Neg}; + +use crate::Assignment; +use crate::prelude::*; + +pub mod bls12; + +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[must_use] +pub struct AffineGadget> { + pub x: F, + pub y: F, + _params: PhantomData

, + _engine: PhantomData, +} + +impl> + AffineGadget +{ + pub fn new(x: F, y: F) -> Self { + Self { + x, + y, + _params: PhantomData, + _engine: PhantomData, + } + } + + pub fn alloc_without_check>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, SynthesisError>, + { + let (x, y) = match value_gen() { + Ok(fe) => { + let fe = fe.into_affine(); + (Ok(fe.x), Ok(fe.y)) + }, + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let x = F::alloc(&mut cs.ns(|| "x"), || x)?; + let y = F::alloc(&mut cs.ns(|| "y"), || y)?; + + Ok(Self::new(x, y)) + } +} + +impl PartialEq for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + fn eq(&self, other: &Self) -> bool { + self.x == other.x && self.y == other.y + } +} + +impl Eq for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ +} + +impl GroupGadget, ConstraintF> for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + type Value = SWProjective

; + type Variable = (F::Variable, F::Variable); + + #[inline] + fn get_value(&self) -> Option { + match (self.x.get_value(), self.y.get_value()) { + (Some(x), Some(y)) => { + let is_zero = x.is_zero() && y.is_one(); + Some(SWAffine::new(x, y, is_zero).into_projective()) + }, + (None, None) => None, + _ => unreachable!(), + } + } + + #[inline] + fn get_variable(&self) -> Self::Variable { + (self.x.get_variable(), self.y.get_variable()) + } + + #[inline] + fn zero>(mut cs: CS) -> Result { + Ok(Self::new( + F::zero(cs.ns(|| "zero"))?, + F::one(cs.ns(|| "one"))?, + )) + } + + #[inline] + /// Incomplete addition: neither `self` nor `other` can be the neutral + /// element. + fn add>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + // lambda = (B.y - A.y)/(B.x - A.x) + // C.x = lambda^2 - A.x - B.x + // C.y = lambda(A.x - C.x) - A.y + // + // Special cases: + // + // doubling: if B.y = A.y and B.x = A.x then lambda is unbound and + // C = (lambda^2, lambda^3) + // + // addition of negative point: if B.y = -A.y and B.x = A.x then no + // lambda can satisfy the first equation unless B.y - A.y = 0. But + // then this reduces to doubling. + // + // So we need to check that A.x - B.x != 0, which can be done by + // enforcing I * (B.x - A.x) = 1 + + let x2_minus_x1 = other.x.sub(cs.ns(|| "x2 - x1"), &self.x)?; + let y2_minus_y1 = other.y.sub(cs.ns(|| "y2 - y1"), &self.y)?; + + let inv = x2_minus_x1.inverse(cs.ns(|| "compute inv"))?; + + let lambda = F::alloc(cs.ns(|| "lambda"), || { + Ok(y2_minus_y1.get_value().get()? * &inv.get_value().get()?) + })?; + + let x_3 = F::alloc(&mut cs.ns(|| "x_3"), || { + let lambda_val = lambda.get_value().get()?; + let x1 = self.x.get_value().get()?; + let x2 = other.x.get_value().get()?; + Ok((lambda_val.square() - &x1) - &x2) + })?; + + let y_3 = F::alloc(&mut cs.ns(|| "y_3"), || { + let lambda_val = lambda.get_value().get()?; + let x_1 = self.x.get_value().get()?; + let y_1 = self.y.get_value().get()?; + let x_3 = x_3.get_value().get()?; + Ok(lambda_val * &(x_1 - &x_3) - &y_1) + })?; + + // Check lambda + lambda.mul_equals(cs.ns(|| "check lambda"), &x2_minus_x1, &y2_minus_y1)?; + + // Check x3 + let x3_plus_x1_plus_x2 = x_3 + .add(cs.ns(|| "x3 + x1"), &self.x)? + .add(cs.ns(|| "x3 + x1 + x2"), &other.x)?; + lambda.mul_equals(cs.ns(|| "check x3"), &lambda, &x3_plus_x1_plus_x2)?; + + // Check y3 + let y3_plus_y1 = y_3.add(cs.ns(|| "y3 + y1"), &self.y)?; + let x1_minus_x3 = self.x.sub(cs.ns(|| "x1 - x3"), &x_3)?; + + lambda.mul_equals(cs.ns(|| ""), &x1_minus_x3, &y3_plus_y1)?; + + Ok(Self::new(x_3, y_3)) + } + + /// Incomplete addition: neither `self` nor `other` can be the neutral + /// element. + fn add_constant>( + &self, + mut cs: CS, + other: &SWProjective

, + ) -> Result { + // lambda = (B.y - A.y)/(B.x - A.x) + // C.x = lambda^2 - A.x - B.x + // C.y = lambda(A.x - C.x) - A.y + // + // Special cases: + // + // doubling: if B.y = A.y and B.x = A.x then lambda is unbound and + // C = (lambda^2, lambda^3) + // + // addition of negative point: if B.y = -A.y and B.x = A.x then no + // lambda can satisfy the first equation unless B.y - A.y = 0. But + // then this reduces to doubling. + // + // So we need to check that A.x - B.x != 0, which can be done by + // enforcing I * (B.x - A.x) = 1 + if other.is_zero() { + return Err(SynthesisError::AssignmentMissing); + } + let other = other.into_affine(); + let other_x = other.x; + let other_y = other.y; + + let x2_minus_x1 = self + .x + .sub_constant(cs.ns(|| "x2 - x1"), &other_x)? + .negate(cs.ns(|| "neg1"))?; + let y2_minus_y1 = self + .y + .sub_constant(cs.ns(|| "y2 - y1"), &other_y)? + .negate(cs.ns(|| "neg2"))?; + + let inv = x2_minus_x1.inverse(cs.ns(|| "compute inv"))?; + + let lambda = F::alloc(cs.ns(|| "lambda"), || { + Ok(y2_minus_y1.get_value().get()? * &inv.get_value().get()?) + })?; + + let x_3 = F::alloc(&mut cs.ns(|| "x_3"), || { + let lambda_val = lambda.get_value().get()?; + let x1 = self.x.get_value().get()?; + let x2 = other_x; + Ok((lambda_val.square() - &x1) - &x2) + })?; + + let y_3 = F::alloc(&mut cs.ns(|| "y_3"), || { + let lambda_val = lambda.get_value().get()?; + let x_1 = self.x.get_value().get()?; + let y_1 = self.y.get_value().get()?; + let x_3 = x_3.get_value().get()?; + Ok(lambda_val * &(x_1 - &x_3) - &y_1) + })?; + + // Check lambda + lambda.mul_equals(cs.ns(|| "check lambda"), &x2_minus_x1, &y2_minus_y1)?; + + // Check x3 + let x3_plus_x1_plus_x2 = x_3 + .add(cs.ns(|| "x3 + x1"), &self.x)? + .add_constant(cs.ns(|| "x3 + x1 + x2"), &other_x)?; + lambda.mul_equals(cs.ns(|| "check x3"), &lambda, &x3_plus_x1_plus_x2)?; + + // Check y3 + let y3_plus_y1 = y_3.add(cs.ns(|| "y3 + y1"), &self.y)?; + let x1_minus_x3 = self.x.sub(cs.ns(|| "x1 - x3"), &x_3)?; + + lambda.mul_equals(cs.ns(|| ""), &x1_minus_x3, &y3_plus_y1)?; + + Ok(Self::new(x_3, y_3)) + } + + #[inline] + fn double_in_place>( + &mut self, + mut cs: CS, + ) -> Result<(), SynthesisError> { + let a = P::COEFF_A; + let x_squared = self.x.square(cs.ns(|| "x^2"))?; + + let one = P::BaseField::one(); + let two = one.double(); + let three = two + &one; + + let three_x_squared = x_squared.mul_by_constant(cs.ns(|| "3 * x^2"), &three)?; + let three_x_squared_plus_a = three_x_squared.add_constant(cs.ns(|| "3 * x^2 + a"), &a)?; + + let two_y = self.y.double(cs.ns(|| "2y"))?; + + let lambda = F::alloc(cs.ns(|| "lambda"), || { + let y_doubled_inv = two_y.get_value().get()?.inverse().get()?; + Ok(three_x_squared_plus_a.get_value().get()? * &y_doubled_inv) + })?; + + // Check lambda + lambda.mul_equals(cs.ns(|| "check lambda"), &two_y, &three_x_squared_plus_a)?; + + let x = lambda + .square(cs.ns(|| "lambda^2"))? + .sub(cs.ns(|| "lambda^2 - x"), &self.x)? + .sub(cs.ns(|| "lambda^2 - 2x"), &self.x)?; + + let y = self + .x + .sub(cs.ns(|| "x - self.x"), &x)? + .mul(cs.ns(|| "times lambda"), &lambda)? + .sub(cs.ns(|| "plus self.y"), &self.y)?; + + *self = Self::new(x, y); + Ok(()) + } + + fn negate>(&self, mut cs: CS) -> Result { + Ok(Self::new( + self.x.clone(), + self.y.negate(cs.ns(|| "negate y"))?, + )) + } + + fn cost_of_add() -> usize { + 3 * F::cost_of_mul() + F::cost_of_inv() + } + + fn cost_of_double() -> usize { + 4 * F::cost_of_mul() + } +} + +impl CondSelectGadget for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + #[inline] + fn conditionally_select>( + mut cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + let x = F::conditionally_select(&mut cs.ns(|| "x"), cond, &first.x, &second.x)?; + let y = F::conditionally_select(&mut cs.ns(|| "y"), cond, &first.y, &second.y)?; + + Ok(Self::new(x, y)) + } + + fn cost() -> usize { + 2 * >::cost() + } +} + +impl EqGadget for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ +} + +impl ConditionalEqGadget for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + #[inline] + fn conditional_enforce_equal>( + &self, + mut cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + self.x.conditional_enforce_equal( + &mut cs.ns(|| "X Coordinate Conditional Equality"), + &other.x, + condition, + )?; + self.y.conditional_enforce_equal( + &mut cs.ns(|| "Y Coordinate Conditional Equality"), + &other.y, + condition, + )?; + Ok(()) + } + + fn cost() -> usize { + 2 * >::cost() + } +} + +impl NEqGadget for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + #[inline] + fn enforce_not_equal>( + &self, + mut cs: CS, + other: &Self, + ) -> Result<(), SynthesisError> { + self.x + .enforce_not_equal(&mut cs.ns(|| "X Coordinate Inequality"), &other.x)?; + self.y + .enforce_not_equal(&mut cs.ns(|| "Y Coordinate Inequality"), &other.y)?; + Ok(()) + } + + fn cost() -> usize { + 2 * >::cost() + } +} + +impl AllocGadget, ConstraintF> for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + #[inline] + fn alloc>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let (x, y) = match value_gen() { + Ok(ge) => { + let ge = ge.borrow().into_affine(); + (Ok(ge.x), Ok(ge.y)) + }, + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + // Perform on-curve check. + let b = P::COEFF_B; + let a = P::COEFF_A; + + let x = F::alloc(&mut cs.ns(|| "x"), || x)?; + let y = F::alloc(&mut cs.ns(|| "y"), || y)?; + + // Check that y^2 = x^3 + ax +b + // We do this by checking that y^2 - b = x * (x^2 +a) + let x2 = x.square(&mut cs.ns(|| "x^2"))?; + let y2 = y.square(&mut cs.ns(|| "y^2"))?; + + let x2_plus_a = x2.add_constant(cs.ns(|| "x^2 + a"), &a)?; + let y2_minus_b = y2.add_constant(cs.ns(|| "y^2 - b"), &b.neg())?; + + x2_plus_a.mul_equals(cs.ns(|| "on curve check"), &x, &y2_minus_b)?; + + Ok(Self::new(x, y)) + } + + #[inline] + fn alloc_checked>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let cofactor_weight = BitIterator::new(P::COFACTOR).filter(|b| *b).count(); + // If we multiply by r, we actually multiply by r - 2. + let r_minus_1 = (-P::ScalarField::one()).into_repr(); + let r_weight = BitIterator::new(&r_minus_1).filter(|b| *b).count(); + + // We pick the most efficient method of performing the prime order check: + // If the cofactor has lower hamming weight than the scalar field's modulus, + // we first multiply by the inverse of the cofactor, and then, after allocating, + // multiply by the cofactor. This ensures the resulting point has no cofactors + // + // Else, we multiply by the scalar field's modulus and ensure that the result + // is zero. + if cofactor_weight < r_weight { + let ge = Self::alloc(cs.ns(|| "Alloc checked"), || { + value_gen().map(|ge| { + ge.borrow() + .into_affine() + .mul_by_cofactor_inv() + .into_projective() + }) + })?; + let mut seen_one = false; + let mut result = Self::zero(cs.ns(|| "result"))?; + for (i, b) in BitIterator::new(P::COFACTOR).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + + let old_seen_one = seen_one; + if seen_one { + result.double_in_place(cs.ns(|| "Double"))?; + } else { + seen_one = b; + } + + if b { + result = if old_seen_one { + result.add(cs.ns(|| "Add"), &ge)? + } else { + ge.clone() + }; + } + } + Ok(result) + } else { + let ge = Self::alloc(cs.ns(|| "Alloc checked"), value_gen)?; + let mut seen_one = false; + let mut result = Self::zero(cs.ns(|| "result"))?; + // Returns bits in big-endian order + for (i, b) in BitIterator::new(r_minus_1).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + + let old_seen_one = seen_one; + if seen_one { + result.double_in_place(cs.ns(|| "Double"))?; + } else { + seen_one = b; + } + + if b { + result = if old_seen_one { + result.add(cs.ns(|| "Add"), &ge)? + } else { + ge.clone() + }; + } + } + let neg_ge = ge.negate(cs.ns(|| "Negate ge"))?; + neg_ge.enforce_equal(cs.ns(|| "Check equals"), &result)?; + Ok(ge) + } + } + + #[inline] + fn alloc_input>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let (x, y) = match value_gen() { + Ok(ge) => { + let ge = ge.borrow().into_affine(); + (Ok(ge.x), Ok(ge.y)) + }, + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let b = P::COEFF_B; + let a = P::COEFF_A; + + let x = F::alloc_input(&mut cs.ns(|| "x"), || x)?; + let y = F::alloc_input(&mut cs.ns(|| "y"), || y)?; + + // Check that y^2 = x^3 + ax +b + // We do this by checking that y^2 - b = x * (x^2 +a) + let x2 = x.square(&mut cs.ns(|| "x^2"))?; + let y2 = y.square(&mut cs.ns(|| "y^2"))?; + + let x2_plus_a = x2.add_constant(cs.ns(|| "x^2 + a"), &a)?; + let y2_minus_b = y2.add_constant(cs.ns(|| "y^2 - b"), &b.neg())?; + + x2_plus_a.mul_equals(cs.ns(|| "on curve check"), &x, &y2_minus_b)?; + + Ok(Self::new(x, y)) + } +} + +impl ToBitsGadget for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { + let mut x_bits = self.x.to_bits(&mut cs.ns(|| "X Coordinate To Bits"))?; + let y_bits = self.y.to_bits(&mut cs.ns(|| "Y Coordinate To Bits"))?; + x_bits.extend_from_slice(&y_bits); + Ok(x_bits) + } + + fn to_bits_strict>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + let mut x_bits = self + .x + .to_bits_strict(&mut cs.ns(|| "X Coordinate To Bits"))?; + let y_bits = self + .y + .to_bits_strict(&mut cs.ns(|| "Y Coordinate To Bits"))?; + x_bits.extend_from_slice(&y_bits); + + Ok(x_bits) + } +} + +impl ToBytesGadget for AffineGadget +where + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + let mut x_bytes = self.x.to_bytes(&mut cs.ns(|| "X Coordinate To Bytes"))?; + let y_bytes = self.y.to_bytes(&mut cs.ns(|| "Y Coordinate To Bytes"))?; + x_bytes.extend_from_slice(&y_bytes); + Ok(x_bytes) + } + + fn to_bytes_strict>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + let mut x_bytes = self + .x + .to_bytes_strict(&mut cs.ns(|| "X Coordinate To Bytes"))?; + let y_bytes = self + .y + .to_bytes_strict(&mut cs.ns(|| "Y Coordinate To Bytes"))?; + x_bytes.extend_from_slice(&y_bytes); + + Ok(x_bytes) + } +} diff --git a/r1cs-std/src/groups/curves/twisted_edwards/edwards_bls12.rs b/r1cs-std/src/groups/curves/twisted_edwards/edwards_bls12.rs new file mode 100644 index 0000000..d59fa91 --- /dev/null +++ b/r1cs-std/src/groups/curves/twisted_edwards/edwards_bls12.rs @@ -0,0 +1,32 @@ +use crate::groups::curves::twisted_edwards::AffineGadget; +use algebra::curves::edwards_bls12::EdwardsParameters; +use algebra::fields::edwards_bls12::fq::Fq; + +use crate::fields::edwards_bls12::FqGadget; + +pub type EdwardsBlsGadget = AffineGadget; + +#[cfg(test)] +mod test { + use super::EdwardsBlsGadget as EdwardsG; + use crate::{ + groups::curves::twisted_edwards::test::{edwards_constraint_costs, edwards_test}, + test_constraint_system::TestConstraintSystem, + }; + use algebra::fields::edwards_bls12::fq::Fq; + use algebra::curves::edwards_bls12::EdwardsParameters; + + #[test] + fn edwards_constraint_costs_test() { + let mut cs = TestConstraintSystem::::new(); + edwards_constraint_costs::<_, EdwardsParameters, EdwardsG, _>(&mut cs); + assert!(cs.is_satisfied()); + } + + #[test] + fn edwards_bls12_gadget_test() { + let mut cs = TestConstraintSystem::::new(); + edwards_test::<_, EdwardsParameters, EdwardsG, _>(&mut cs); + assert!(cs.is_satisfied()); + } +} diff --git a/r1cs-std/src/groups/curves/twisted_edwards/edwards_sw6.rs b/r1cs-std/src/groups/curves/twisted_edwards/edwards_sw6.rs new file mode 100644 index 0000000..e903eeb --- /dev/null +++ b/r1cs-std/src/groups/curves/twisted_edwards/edwards_sw6.rs @@ -0,0 +1,32 @@ +use crate::groups::curves::twisted_edwards::AffineGadget; +use algebra::curves::edwards_sw6::EdwardsParameters; +use algebra::fields::edwards_sw6::fq::Fq; + +use crate::fields::edwards_sw6::FqGadget; + +pub type EdwardsSWGadget = AffineGadget; + +#[cfg(test)] +mod test { + use super::EdwardsSWGadget as EdwardsG; + use crate::{ + groups::curves::twisted_edwards::test::{edwards_constraint_costs, edwards_test}, + test_constraint_system::TestConstraintSystem, + }; + use algebra::curves::edwards_sw6::EdwardsParameters; + use algebra::fields::edwards_sw6::fq::Fq; + + #[test] + fn edwards_constraint_costs_test() { + let mut cs = TestConstraintSystem::::new(); + edwards_constraint_costs::<_, EdwardsParameters, EdwardsG, _>(&mut cs); + assert!(cs.is_satisfied()); + } + + #[test] + fn edwards_sw6_gadget_test() { + let mut cs = TestConstraintSystem::::new(); + edwards_test::<_, EdwardsParameters, EdwardsG, _>(&mut cs); + assert!(cs.is_satisfied()); + } +} diff --git a/r1cs-std/src/groups/curves/twisted_edwards/jubjub.rs b/r1cs-std/src/groups/curves/twisted_edwards/jubjub.rs new file mode 100644 index 0000000..4cfbf50 --- /dev/null +++ b/r1cs-std/src/groups/curves/twisted_edwards/jubjub.rs @@ -0,0 +1,32 @@ +use crate::groups::curves::twisted_edwards::AffineGadget; +use algebra::curves::jubjub::JubJubParameters; +use algebra::fields::jubjub::fq::Fq; + +use crate::fields::jubjub::FqGadget; + +pub type JubJubGadget = AffineGadget; + +#[cfg(test)] +mod test { + use super::JubJubGadget as EdwardsG; + use crate::{ + groups::curves::twisted_edwards::test::{edwards_constraint_costs, edwards_test}, + test_constraint_system::TestConstraintSystem, + }; + use algebra::fields::jubjub::fq::Fq; + use algebra::curves::jubjub::JubJubParameters as EdwardsParameters; + + #[test] + fn edwards_constraint_costs_test() { + let mut cs = TestConstraintSystem::::new(); + edwards_constraint_costs::<_, EdwardsParameters, EdwardsG, _>(&mut cs); + assert!(cs.is_satisfied()); + } + + #[test] + fn jubjub_gadget_test() { + let mut cs = TestConstraintSystem::::new(); + edwards_test::<_, EdwardsParameters, EdwardsG, _>(&mut cs); + assert!(cs.is_satisfied()); + } +} diff --git a/r1cs-std/src/groups/curves/twisted_edwards/mod.rs b/r1cs-std/src/groups/curves/twisted_edwards/mod.rs new file mode 100644 index 0000000..f0e77bd --- /dev/null +++ b/r1cs-std/src/groups/curves/twisted_edwards/mod.rs @@ -0,0 +1,1078 @@ +use algebra::{ + curves::{twisted_edwards_extended::GroupAffine as TEAffine, TEModelParameters}, + BitIterator, Field +}; + +use r1cs_core::{ConstraintSystem, SynthesisError}; + +use crate::prelude::*; + +use std::{borrow::Borrow, marker::PhantomData}; + +pub mod edwards_bls12; +pub mod edwards_sw6; +pub mod jubjub; +#[cfg(test)] +mod test; + +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[derivative(Debug(bound = "P: TEModelParameters, ConstraintF: Field"))] +#[must_use] +pub struct AffineGadget> { + pub x: F, + pub y: F, + #[derivative(Debug = "ignore")] + _params: PhantomData

, + #[derivative(Debug = "ignore")] + _engine: PhantomData, +} + +impl> + AffineGadget +{ + pub fn new(x: F, y: F) -> Self { + Self { + x, + y, + _params: PhantomData, + _engine: PhantomData, + } + } + + pub fn alloc_without_check>( + mut cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, SynthesisError>, + { + let (x, y) = match value_gen() { + Ok(fe) => (Ok(fe.x), Ok(fe.y)), + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let x = F::alloc(&mut cs.ns(|| "x"), || x)?; + let y = F::alloc(&mut cs.ns(|| "y"), || y)?; + + Ok(Self::new(x, y)) + } +} + +impl PartialEq for AffineGadget +where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + fn eq(&self, other: &Self) -> bool { + self.x == other.x && self.y == other.y + } +} + +impl Eq for AffineGadget +where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ +} + +mod affine_impl { + use super::*; + use crate::Assignment; + use algebra::{curves::AffineCurve, Field, PrimeField}; + use std::ops::Neg; + + impl GroupGadget, ConstraintF> for AffineGadget + where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, + { + type Value = TEAffine

; + type Variable = (F::Variable, F::Variable); + + #[inline] + fn get_value(&self) -> Option { + match (self.x.get_value(), self.y.get_value()) { + (Some(x), Some(y)) => Some(TEAffine::new(x, y)), + (..) => None, + } + } + + #[inline] + fn get_variable(&self) -> Self::Variable { + (self.x.get_variable(), self.y.get_variable()) + } + + #[inline] + fn zero>(mut cs: CS) -> Result { + Ok(Self::new( + F::zero(cs.ns(|| "zero"))?, + F::one(cs.ns(|| "one"))?, + )) + } + + /// Optimized constraints for checking Edwards point addition from ZCash + /// developers Daira Hopwood and Sean Bowe. Requires only 6 constraints + /// compared to 7 for the straightforward version we had earlier. + fn add>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + let a = P::COEFF_A; + let d = P::COEFF_D; + + // Compute U = (x1 + y1) * (x2 + y2) + let u1 = self + .x + .mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())? + .add(cs.ns(|| "-A * x1 + y1"), &self.y)?; + let u2 = other.x.add(cs.ns(|| "x2 + y2"), &other.y)?; + + let u = u1.mul(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?; + + // Compute v0 = x1 * y2 + let v0 = other.y.mul(&mut cs.ns(|| "v0"), &self.x)?; + + // Compute v1 = x2 * y1 + let v1 = other.x.mul(&mut cs.ns(|| "v1"), &self.y)?; + + // Compute C = d*v0*v1 + let v2 = v0 + .mul(cs.ns(|| "v0 * v1"), &v1)? + .mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?; + + // Compute x3 = (v0 + v1) / (1 + v2) + let x3 = F::alloc(&mut cs.ns(|| "x3"), || { + let t0 = v0.get_value().get()? + &v1.get_value().get()?; + let t1 = P::BaseField::one() + &v2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let one = P::BaseField::one(); + let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?; + let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?; + x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?; + + // Compute y3 = (U + a * v0 - v1) / (1 - v2) + let y3 = F::alloc(&mut cs.ns(|| "y3"), || { + let t0 = + u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?; + let t1 = P::BaseField::one() - &v2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let one_minus_v2 = v2 + .add_constant(cs.ns(|| "v2 - 1"), &(-one))? + .negate(cs.ns(|| "1 - v2"))?; + let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?; + let u_plus_a_v0_minus_v1 = u + .add(cs.ns(|| "u + a * v0"), &a_v0)? + .sub(cs.ns(|| "u + a * v0 - v1"), &v1)?; + + y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?; + + Ok(Self::new(x3, y3)) + } + + fn add_constant>( + &self, + mut cs: CS, + other: &TEAffine

, + ) -> Result { + let a = P::COEFF_A; + let d = P::COEFF_D; + let other_x = other.x; + let other_y = other.y; + + // Compute U = (x1 + y1) * (x2 + y2) + let u1 = self + .x + .mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())? + .add(cs.ns(|| "-A * x1 + y1"), &self.y)?; + let u2 = other_x + &other_y; + + let u = u1.mul_by_constant(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?; + + // Compute v0 = x1 * y2 + let v0 = self.x.mul_by_constant(&mut cs.ns(|| "v0"), &other_y)?; + + // Compute v1 = x2 * y1 + let v1 = self.y.mul_by_constant(&mut cs.ns(|| "v1"), &other.x)?; + + // Compute C = d*v0*v1 + let v2 = v0 + .mul(cs.ns(|| "v0 * v1"), &v1)? + .mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?; + + // Compute x3 = (v0 + v1) / (1 + v2) + let x3 = F::alloc(&mut cs.ns(|| "x3"), || { + let t0 = v0.get_value().get()? + &v1.get_value().get()?; + let t1 = P::BaseField::one() + &v2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let one = P::BaseField::one(); + let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?; + let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?; + x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?; + + // Compute y3 = (U + a * v0 - v1) / (1 - v2) + let y3 = F::alloc(&mut cs.ns(|| "y3"), || { + let t0 = + u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?; + let t1 = P::BaseField::one() - &v2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let one_minus_v2 = v2 + .add_constant(cs.ns(|| "v2 - 1"), &(-one))? + .negate(cs.ns(|| "1 - v2"))?; + let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?; + let u_plus_a_v0_minus_v1 = u + .add(cs.ns(|| "u + a * v0"), &a_v0)? + .sub(cs.ns(|| "u + a * v0 - v1"), &v1)?; + + y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?; + + Ok(Self::new(x3, y3)) + } + + fn double_in_place>( + &mut self, + mut cs: CS, + ) -> Result<(), SynthesisError> { + let a = P::COEFF_A; + + // xy + let xy = self.x.mul(cs.ns(|| "x * y"), &self.y)?; + let x2 = self.x.square(cs.ns(|| "x * x"))?; + let y2 = self.y.square(cs.ns(|| "y * y"))?; + + let a_x2 = x2.mul_by_constant(cs.ns(|| "a * x^2"), &a)?; + + // Compute x3 = (2xy) / (ax^2 + y^2) + let x3 = F::alloc(&mut cs.ns(|| "x3"), || { + let t0 = xy.get_value().get()?.double(); + let t1 = a * &x2.get_value().get()? + &y2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let a_x2_plus_y2 = a_x2.add(cs.ns(|| "v2 + 1"), &y2)?; + let two_xy = xy.double(cs.ns(|| "2xy"))?; + x3.mul_equals(cs.ns(|| "check x3"), &a_x2_plus_y2, &two_xy)?; + + // Compute y3 = (y^2 - ax^2) / (2 - ax^2 - y^2) + let two = P::BaseField::one().double(); + let y3 = F::alloc(&mut cs.ns(|| "y3"), || { + let a_x2 = a * &x2.get_value().get()?; + let t0 = y2.get_value().get()? - &a_x2; + let t1 = two - &a_x2 - &y2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + let y2_minus_a_x2 = y2.sub(cs.ns(|| "y^2 - ax^2"), &a_x2)?; + let two_minus_ax2_minus_y2 = a_x2 + .add(cs.ns(|| "ax2 + y2"), &y2)? + .negate(cs.ns(|| "-ax2 - y2"))? + .add_constant(cs.ns(|| "2 -ax2 - y2"), &two)?; + + y3.mul_equals( + cs.ns(|| "check y3"), + &two_minus_ax2_minus_y2, + &y2_minus_a_x2, + )?; + self.x = x3; + self.y = y3; + + Ok(()) + } + + fn negate>(&self, mut cs: CS) -> Result { + Ok(Self::new( + self.x.negate(cs.ns(|| "negate x"))?, + self.y.clone(), + )) + } + + fn cost_of_add() -> usize { + 4 + 2 * F::cost_of_mul() + } + + fn cost_of_double() -> usize { + 4 + F::cost_of_mul() + } + } + + impl AllocGadget, ConstraintF> for AffineGadget + where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, + Self: GroupGadget, ConstraintF>, + { + fn alloc>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let (x, y) = match value_gen() { + Ok(ge) => { + let ge = *ge.borrow(); + (Ok(ge.x), Ok(ge.y)) + }, + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let d = P::COEFF_D; + let a = P::COEFF_A; + + let x = F::alloc(&mut cs.ns(|| "x"), || x)?; + let y = F::alloc(&mut cs.ns(|| "y"), || y)?; + + // Check that ax^2 + y^2 = 1 + dx^2y^2 + // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) + let x2 = x.square(&mut cs.ns(|| "x^2"))?; + let y2 = y.square(&mut cs.ns(|| "y^2"))?; + + let one = P::BaseField::one(); + let d_x2_minus_one = x2 + .mul_by_constant(cs.ns(|| "d * x^2"), &d)? + .add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?; + + let a_x2_minus_one = x2 + .mul_by_constant(cs.ns(|| "a * x^2"), &a)? + .add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?; + + d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?; + Ok(Self::new(x, y)) + } + + fn alloc_checked>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let cofactor_weight = BitIterator::new(P::COFACTOR).filter(|b| *b).count(); + // If we multiply by r, we actually multiply by r - 2. + let r_minus_1 = (-P::ScalarField::one()).into_repr(); + let r_weight = BitIterator::new(&r_minus_1).filter(|b| *b).count(); + + // We pick the most efficient method of performing the prime order check: + // If the cofactor has lower hamming weight than the scalar field's modulus, + // we first multiply by the inverse of the cofactor, and then, after allocating, + // multiply by the cofactor. This ensures the resulting point has no cofactors + // + // Else, we multiply by the scalar field's modulus and ensure that the result + // is zero. + if cofactor_weight < r_weight { + let ge = Self::alloc(cs.ns(|| "Alloc checked"), || { + value_gen().map(|ge| ge.borrow().mul_by_cofactor_inv()) + })?; + let mut seen_one = false; + let mut result = Self::zero(cs.ns(|| "result"))?; + for (i, b) in BitIterator::new(P::COFACTOR).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + + let old_seen_one = seen_one; + if seen_one { + result.double_in_place(cs.ns(|| "Double"))?; + } else { + seen_one = b; + } + + if b { + result = if old_seen_one { + result.add(cs.ns(|| "Add"), &ge)? + } else { + ge.clone() + }; + } + } + Ok(result) + } else { + let ge = Self::alloc(cs.ns(|| "Alloc checked"), value_gen)?; + let mut seen_one = false; + let mut result = Self::zero(cs.ns(|| "result"))?; + // Returns bits in big-endian order + for (i, b) in BitIterator::new(r_minus_1).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + + let old_seen_one = seen_one; + if seen_one { + result.double_in_place(cs.ns(|| "Double"))?; + } else { + seen_one = b; + } + + if b { + result = if old_seen_one { + result.add(cs.ns(|| "Add"), &ge)? + } else { + ge.clone() + }; + } + } + let neg_ge = ge.negate(cs.ns(|| "Negate ge"))?; + neg_ge.enforce_equal(cs.ns(|| "Check equals"), &result)?; + Ok(ge) + } + } + + fn alloc_input>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let (x, y) = match value_gen() { + Ok(ge) => { + let ge = *ge.borrow(); + (Ok(ge.x), Ok(ge.y)) + }, + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let d = P::COEFF_D; + let a = P::COEFF_A; + + let x = F::alloc_input(&mut cs.ns(|| "x"), || x)?; + let y = F::alloc_input(&mut cs.ns(|| "y"), || y)?; + + // Check that ax^2 + y^2 = 1 + dx^2y^2 + // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) + let x2 = x.square(&mut cs.ns(|| "x^2"))?; + let y2 = y.square(&mut cs.ns(|| "y^2"))?; + + let one = P::BaseField::one(); + let d_x2_minus_one = x2 + .mul_by_constant(cs.ns(|| "d * x^2"), &d)? + .add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?; + + let a_x2_minus_one = x2 + .mul_by_constant(cs.ns(|| "a * x^2"), &a)? + .add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?; + + d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?; + Ok(Self::new(x, y)) + } + } +} + +mod projective_impl { + use super::*; + use crate::Assignment; + use algebra::{ + curves::twisted_edwards_extended::GroupProjective as TEProjective, AffineCurve, Field, + PrimeField, ProjectiveCurve, + }; + use std::ops::Neg; + + impl GroupGadget, ConstraintF> for AffineGadget + where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, + { + type Value = TEProjective

; + type Variable = (F::Variable, F::Variable); + + #[inline] + fn get_value(&self) -> Option { + match (self.x.get_value(), self.y.get_value()) { + (Some(x), Some(y)) => Some(TEAffine::new(x, y).into()), + (..) => None, + } + } + + #[inline] + fn get_variable(&self) -> Self::Variable { + (self.x.get_variable(), self.y.get_variable()) + } + + #[inline] + fn zero>(mut cs: CS) -> Result { + Ok(Self::new( + F::zero(cs.ns(|| "zero"))?, + F::one(cs.ns(|| "one"))?, + )) + } + + /// Optimized constraints for checking Edwards point addition from ZCash + /// developers Daira Hopwood and Sean Bowe. Requires only 6 constraints + /// compared to 7 for the straightforward version we had earlier. + fn add>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + let a = P::COEFF_A; + let d = P::COEFF_D; + + // Compute U = (x1 + y1) * (x2 + y2) + let u1 = self + .x + .mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())? + .add(cs.ns(|| "-A * x1 + y1"), &self.y)?; + let u2 = other.x.add(cs.ns(|| "x2 + y2"), &other.y)?; + + let u = u1.mul(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?; + + // Compute v0 = x1 * y2 + let v0 = other.y.mul(&mut cs.ns(|| "v0"), &self.x)?; + + // Compute v1 = x2 * y1 + let v1 = other.x.mul(&mut cs.ns(|| "v1"), &self.y)?; + + // Compute C = d*v0*v1 + let v2 = v0 + .mul(cs.ns(|| "v0 * v1"), &v1)? + .mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?; + + // Compute x3 = (v0 + v1) / (1 + v2) + let x3 = F::alloc(&mut cs.ns(|| "x3"), || { + let t0 = v0.get_value().get()? + &v1.get_value().get()?; + let t1 = P::BaseField::one() + &v2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let one = P::BaseField::one(); + let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?; + let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?; + x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?; + + // Compute y3 = (U + a * v0 - v1) / (1 - v2) + let y3 = F::alloc(&mut cs.ns(|| "y3"), || { + let t0 = + u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?; + let t1 = P::BaseField::one() - &v2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let one_minus_v2 = v2 + .add_constant(cs.ns(|| "v2 - 1"), &(-one))? + .negate(cs.ns(|| "1 - v2"))?; + let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?; + let u_plus_a_v0_minus_v1 = u + .add(cs.ns(|| "u + a * v0"), &a_v0)? + .sub(cs.ns(|| "u + a * v0 - v1"), &v1)?; + + y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?; + + Ok(Self::new(x3, y3)) + } + + fn add_constant>( + &self, + mut cs: CS, + other: &TEProjective

, + ) -> Result { + let a = P::COEFF_A; + let d = P::COEFF_D; + let other = other.into_affine(); + let other_x = other.x; + let other_y = other.y; + + // Compute U = (x1 + y1) * (x2 + y2) + let u1 = self + .x + .mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())? + .add(cs.ns(|| "-A * x1 + y1"), &self.y)?; + let u2 = other_x + &other_y; + + let u = u1.mul_by_constant(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?; + + // Compute v0 = x1 * y2 + let v0 = self.x.mul_by_constant(&mut cs.ns(|| "v0"), &other_y)?; + + // Compute v1 = x2 * y1 + let v1 = self.y.mul_by_constant(&mut cs.ns(|| "v1"), &other.x)?; + + // Compute C = d*v0*v1 + let v2 = v0 + .mul(cs.ns(|| "v0 * v1"), &v1)? + .mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?; + + // Compute x3 = (v0 + v1) / (1 + v2) + let x3 = F::alloc(&mut cs.ns(|| "x3"), || { + let t0 = v0.get_value().get()? + &v1.get_value().get()?; + let t1 = P::BaseField::one() + &v2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let one = P::BaseField::one(); + let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?; + let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?; + x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?; + + // Compute y3 = (U + a * v0 - v1) / (1 - v2) + let y3 = F::alloc(&mut cs.ns(|| "y3"), || { + let t0 = + u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?; + let t1 = P::BaseField::one() - &v2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let one_minus_v2 = v2 + .add_constant(cs.ns(|| "v2 - 1"), &(-one))? + .negate(cs.ns(|| "1 - v2"))?; + let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?; + let u_plus_a_v0_minus_v1 = u + .add(cs.ns(|| "u + a * v0"), &a_v0)? + .sub(cs.ns(|| "u + a * v0 - v1"), &v1)?; + + y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?; + + Ok(Self::new(x3, y3)) + } + + fn double_in_place>( + &mut self, + mut cs: CS, + ) -> Result<(), SynthesisError> { + let a = P::COEFF_A; + + // xy + let xy = self.x.mul(cs.ns(|| "x * y"), &self.y)?; + let x2 = self.x.square(cs.ns(|| "x * x"))?; + let y2 = self.y.square(cs.ns(|| "y * y"))?; + + let a_x2 = x2.mul_by_constant(cs.ns(|| "a * x^2"), &a)?; + + // Compute x3 = (2xy) / (ax^2 + y^2) + let x3 = F::alloc(&mut cs.ns(|| "x3"), || { + let t0 = xy.get_value().get()?.double(); + let t1 = a * &x2.get_value().get()? + &y2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + + let a_x2_plus_y2 = a_x2.add(cs.ns(|| "v2 + 1"), &y2)?; + let two_xy = xy.double(cs.ns(|| "2xy"))?; + x3.mul_equals(cs.ns(|| "check x3"), &a_x2_plus_y2, &two_xy)?; + + // Compute y3 = (y^2 - ax^2) / (2 - ax^2 - y^2) + let two = P::BaseField::one().double(); + let y3 = F::alloc(&mut cs.ns(|| "y3"), || { + let a_x2 = a * &x2.get_value().get()?; + let t0 = y2.get_value().get()? - &a_x2; + let t1 = two - &a_x2 - &y2.get_value().get()?; + Ok(t0 * &t1.inverse().get()?) + })?; + let y2_minus_a_x2 = y2.sub(cs.ns(|| "y^2 - ax^2"), &a_x2)?; + let two_minus_ax2_minus_y2 = a_x2 + .add(cs.ns(|| "ax2 + y2"), &y2)? + .negate(cs.ns(|| "-ax2 - y2"))? + .add_constant(cs.ns(|| "2 -ax2 - y2"), &two)?; + + y3.mul_equals( + cs.ns(|| "check y3"), + &two_minus_ax2_minus_y2, + &y2_minus_a_x2, + )?; + self.x = x3; + self.y = y3; + + Ok(()) + } + + fn negate>(&self, mut cs: CS) -> Result { + Ok(Self::new( + self.x.negate(cs.ns(|| "negate x"))?, + self.y.clone(), + )) + } + + fn precomputed_base_scalar_mul<'a, CS, I, B>( + &mut self, + mut cs: CS, + scalar_bits_with_base_powers: I, + ) -> Result<(), SynthesisError> + where + CS: ConstraintSystem, + I: Iterator)>, + B: Borrow, + { + let scalar_bits_with_base_powers: Vec<_> = scalar_bits_with_base_powers + .map(|(bit, base)| (bit.borrow().clone(), base.clone())) + .collect(); + let zero = TEProjective::zero(); + for (i, bits_base_powers) in scalar_bits_with_base_powers.chunks(2).enumerate() { + let mut cs = cs.ns(|| format!("Chunk {}", i)); + if bits_base_powers.len() == 2 { + let bits = [bits_base_powers[0].0, bits_base_powers[1].0]; + let base_powers = [bits_base_powers[0].1, bits_base_powers[1].1]; + + let mut table = [ + zero, + base_powers[0], + base_powers[1], + base_powers[0] + &base_powers[1], + ]; + + TEProjective::batch_normalization(&mut table); + let x_s = [table[0].x, table[1].x, table[2].x, table[3].x]; + let y_s = [table[0].y, table[1].y, table[2].y, table[3].y]; + + let x: F = F::two_bit_lookup(cs.ns(|| "Lookup x"), &bits, &x_s)?; + let y: F = F::two_bit_lookup(cs.ns(|| "Lookup y"), &bits, &y_s)?; + let adder: Self = Self::new(x, y); + *self = , ConstraintF>>::add( + self, + &mut cs.ns(|| "Add"), + &adder, + )?; + } else if bits_base_powers.len() == 1 { + let bit = bits_base_powers[0].0; + let base_power = bits_base_powers[0].1; + let new_encoded = + self.add_constant(&mut cs.ns(|| "Add base power"), &base_power)?; + *self = Self::conditionally_select( + &mut cs.ns(|| "Conditional Select"), + &bit, + &new_encoded, + &self, + )?; + } + } + + Ok(()) + } + + fn cost_of_add() -> usize { + 4 + 2 * F::cost_of_mul() + } + + fn cost_of_double() -> usize { + 4 + F::cost_of_mul() + } + } + + impl AllocGadget, ConstraintF> for AffineGadget + where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, + Self: GroupGadget, ConstraintF>, + { + fn alloc>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let (x, y) = match value_gen() { + Ok(ge) => { + let ge = ge.borrow().into_affine(); + (Ok(ge.x), Ok(ge.y)) + }, + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let d = P::COEFF_D; + let a = P::COEFF_A; + + let x = F::alloc(&mut cs.ns(|| "x"), || x)?; + let y = F::alloc(&mut cs.ns(|| "y"), || y)?; + + // Check that ax^2 + y^2 = 1 + dx^2y^2 + // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) + let x2 = x.square(&mut cs.ns(|| "x^2"))?; + let y2 = y.square(&mut cs.ns(|| "y^2"))?; + + let one = P::BaseField::one(); + let d_x2_minus_one = x2 + .mul_by_constant(cs.ns(|| "d * x^2"), &d)? + .add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?; + + let a_x2_minus_one = x2 + .mul_by_constant(cs.ns(|| "a * x^2"), &a)? + .add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?; + + d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?; + Ok(Self::new(x, y)) + } + + fn alloc_checked>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let cofactor_weight = BitIterator::new(P::COFACTOR).filter(|b| *b).count(); + // If we multiply by r, we actually multiply by r - 2. + let r_minus_1 = (-P::ScalarField::one()).into_repr(); + let r_weight = BitIterator::new(&r_minus_1).filter(|b| *b).count(); + + // We pick the most efficient method of performing the prime order check: + // If the cofactor has lower hamming weight than the scalar field's modulus, + // we first multiply by the inverse of the cofactor, and then, after allocating, + // multiply by the cofactor. This ensures the resulting point has no cofactors + // + // Else, we multiply by the scalar field's modulus and ensure that the result + // is zero. + if cofactor_weight < r_weight { + let ge = Self::alloc(cs.ns(|| "Alloc checked"), || { + value_gen().map(|ge| { + ge.borrow() + .into_affine() + .mul_by_cofactor_inv() + .into_projective() + }) + })?; + let mut seen_one = false; + let mut result = Self::zero(cs.ns(|| "result"))?; + for (i, b) in BitIterator::new(P::COFACTOR).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + + let old_seen_one = seen_one; + if seen_one { + result.double_in_place(cs.ns(|| "Double"))?; + } else { + seen_one = b; + } + + if b { + result = if old_seen_one { + result.add(cs.ns(|| "Add"), &ge)? + } else { + ge.clone() + }; + } + } + Ok(result) + } else { + let ge = Self::alloc(cs.ns(|| "Alloc checked"), value_gen)?; + let mut seen_one = false; + let mut result = Self::zero(cs.ns(|| "result"))?; + // Returns bits in big-endian order + for (i, b) in BitIterator::new(r_minus_1).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", i)); + + let old_seen_one = seen_one; + if seen_one { + result.double_in_place(cs.ns(|| "Double"))?; + } else { + seen_one = b; + } + + if b { + result = if old_seen_one { + result.add(cs.ns(|| "Add"), &ge)? + } else { + ge.clone() + }; + } + } + let neg_ge = ge.negate(cs.ns(|| "Negate ge"))?; + neg_ge.enforce_equal(cs.ns(|| "Check equals"), &result)?; + Ok(ge) + } + } + + fn alloc_input>( + mut cs: CS, + value_gen: FN, + ) -> Result + where + FN: FnOnce() -> Result, + T: Borrow>, + { + let (x, y) = match value_gen() { + Ok(ge) => { + let ge = ge.borrow().into_affine(); + (Ok(ge.x), Ok(ge.y)) + }, + _ => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let d = P::COEFF_D; + let a = P::COEFF_A; + + let x = F::alloc_input(&mut cs.ns(|| "x"), || x)?; + let y = F::alloc_input(&mut cs.ns(|| "y"), || y)?; + + // Check that ax^2 + y^2 = 1 + dx^2y^2 + // We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1) + let x2 = x.square(&mut cs.ns(|| "x^2"))?; + let y2 = y.square(&mut cs.ns(|| "y^2"))?; + + let one = P::BaseField::one(); + let d_x2_minus_one = x2 + .mul_by_constant(cs.ns(|| "d * x^2"), &d)? + .add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?; + + let a_x2_minus_one = x2 + .mul_by_constant(cs.ns(|| "a * x^2"), &a)? + .add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?; + + d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?; + Ok(Self::new(x, y)) + } + } +} + +impl CondSelectGadget for AffineGadget +where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + #[inline] + fn conditionally_select>( + mut cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + let x = F::conditionally_select(&mut cs.ns(|| "x"), cond, &first.x, &second.x)?; + let y = F::conditionally_select(&mut cs.ns(|| "y"), cond, &first.y, &second.y)?; + + Ok(Self::new(x, y)) + } + + fn cost() -> usize { + 2 * >::cost() + } +} + +impl EqGadget for AffineGadget +where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ +} + +impl ConditionalEqGadget for AffineGadget +where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + #[inline] + fn conditional_enforce_equal>( + &self, + mut cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + self.x.conditional_enforce_equal( + &mut cs.ns(|| "X Coordinate Conditional Equality"), + &other.x, + condition, + )?; + self.y.conditional_enforce_equal( + &mut cs.ns(|| "Y Coordinate Conditional Equality"), + &other.y, + condition, + )?; + Ok(()) + } + + fn cost() -> usize { + 2 * >::cost() + } +} + +impl NEqGadget for AffineGadget +where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + #[inline] + fn enforce_not_equal>( + &self, + mut cs: CS, + other: &Self, + ) -> Result<(), SynthesisError> { + self.x + .enforce_not_equal(&mut cs.ns(|| "X Coordinate Inequality"), &other.x)?; + self.y + .enforce_not_equal(&mut cs.ns(|| "Y Coordinate Inequality"), &other.y)?; + Ok(()) + } + + fn cost() -> usize { + 2 * >::cost() + } +} + +impl ToBitsGadget for AffineGadget +where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { + let mut x_bits = self.x.to_bits(cs.ns(|| "X Coordinate To Bits"))?; + let y_bits = self.y.to_bits(cs.ns(|| "Y Coordinate To Bits"))?; + x_bits.extend_from_slice(&y_bits); + Ok(x_bits) + } + + fn to_bits_strict>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + let mut x_bits = self.x.to_bits_strict(cs.ns(|| "X Coordinate To Bits"))?; + let y_bits = self.y.to_bits_strict(cs.ns(|| "Y Coordinate To Bits"))?; + x_bits.extend_from_slice(&y_bits); + + Ok(x_bits) + } +} + +impl ToBytesGadget for AffineGadget +where + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +{ + fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + let mut x_bytes = self.x.to_bytes(cs.ns(|| "x"))?; + let y_bytes = self.y.to_bytes(cs.ns(|| "y"))?; + x_bytes.extend_from_slice(&y_bytes); + Ok(x_bytes) + } + + fn to_bytes_strict>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + let mut x_bytes = self.x.to_bytes_strict(cs.ns(|| "x"))?; + let y_bytes = self.y.to_bytes_strict(cs.ns(|| "y"))?; + x_bytes.extend_from_slice(&y_bytes); + + Ok(x_bytes) + } +} diff --git a/r1cs-std/src/groups/curves/twisted_edwards/test.rs b/r1cs-std/src/groups/curves/twisted_edwards/test.rs new file mode 100644 index 0000000..5a3f6b1 --- /dev/null +++ b/r1cs-std/src/groups/curves/twisted_edwards/test.rs @@ -0,0 +1,77 @@ +use rand; + +use crate::{ + boolean::Boolean, + groups::{test::group_test, GroupGadget}, + utils::{AllocGadget, CondSelectGadget}, +}; + +use algebra::{ + curves::{models::TEModelParameters, twisted_edwards_extended::GroupAffine as TEAffine}, + BitIterator, Group, PrimeField, Field, +}; + +use r1cs_core::ConstraintSystem; + +pub(crate) fn edwards_test(cs: &mut CS) +where + ConstraintF: Field, + P: TEModelParameters, + GG: GroupGadget, ConstraintF, Value = TEAffine

>, + CS: ConstraintSystem, +{ + let a: TEAffine

= rand::random(); + let b: TEAffine

= rand::random(); + let gadget_a = GG::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); + let gadget_b = GG::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); + assert_eq!(gadget_a.get_value().unwrap(), a); + assert_eq!(gadget_b.get_value().unwrap(), b); + group_test::, GG, _>( + &mut cs.ns(|| "GroupTest(a, b)"), + gadget_a.clone(), + gadget_b, + ); + + // Check mul_bits + let scalar: as Group>::ScalarField = rand::random(); + let native_result = a.mul(&scalar); + + let mut scalar: Vec = BitIterator::new(scalar.into_repr()).collect(); + // Get the scalar bits into little-endian form. + scalar.reverse(); + let input = Vec::::alloc(cs.ns(|| "Input"), || Ok(scalar)).unwrap(); + let zero = GG::zero(cs.ns(|| "zero")).unwrap(); + let result = gadget_a + .mul_bits(cs.ns(|| "mul_bits"), &zero, input.iter()) + .unwrap(); + let gadget_value = result.get_value().expect("Gadget_result failed"); + assert_eq!(native_result, gadget_value); +} + +pub(crate) fn edwards_constraint_costs(cs: &mut CS) +where + ConstraintF: Field, + P: TEModelParameters, + GG: GroupGadget, ConstraintF, Value = TEAffine

>, + CS: ConstraintSystem, +{ + use crate::boolean::AllocatedBit; + + let bit = AllocatedBit::alloc(&mut cs.ns(|| "bool"), || Ok(true)) + .unwrap() + .into(); + + let a: TEAffine

= rand::random(); + let b: TEAffine

= rand::random(); + let gadget_a = GG::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); + let gadget_b = GG::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); + let alloc_cost = cs.num_constraints(); + let _ = + GG::conditionally_select(&mut cs.ns(|| "cond_select"), &bit, &gadget_a, &gadget_b).unwrap(); + let cond_select_cost = cs.num_constraints() - alloc_cost; + + let _ = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap(); + let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost; + assert_eq!(cond_select_cost, >::cost()); + assert_eq!(add_cost, GG::cost_of_add()); +} diff --git a/r1cs-std/src/groups/mod.rs b/r1cs-std/src/groups/mod.rs new file mode 100644 index 0000000..4176dd9 --- /dev/null +++ b/r1cs-std/src/groups/mod.rs @@ -0,0 +1,218 @@ +use crate::prelude::*; +use algebra::{Group, Field}; +use r1cs_core::{ConstraintSystem, SynthesisError}; + +use std::{borrow::Borrow, fmt::Debug}; + +pub mod curves; + +pub use self::curves::{ + short_weierstrass::bls12, + twisted_edwards::{edwards_sw6, jubjub}, +}; + +pub trait GroupGadget: + Sized + + ToBytesGadget + + NEqGadget + + EqGadget + + ToBitsGadget + + CondSelectGadget + + AllocGadget + + Clone + + Debug +{ + type Value: Debug; + type Variable; + + fn get_value(&self) -> Option; + + fn get_variable(&self) -> Self::Variable; + + fn zero>(cs: CS) -> Result; + + fn add>(&self, cs: CS, other: &Self) -> Result; + + fn sub>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + let neg_other = other.negate(cs.ns(|| "Negate other"))?; + self.add(cs.ns(|| "Self - other"), &neg_other) + } + + fn add_constant>( + &self, + cs: CS, + other: &G, + ) -> Result; + + fn sub_constant>( + &self, + mut cs: CS, + other: &G, + ) -> Result { + let neg_other = -(*other); + self.add_constant(cs.ns(|| "Self - other"), &neg_other) + } + + fn double_in_place>(&mut self, cs: CS) -> Result<(), SynthesisError>; + + fn negate>(&self, cs: CS) -> Result; + + /// Inputs must be specified in *little-endian* form. + /// If the addition law is incomplete for the identity element, + /// `result` must not be the identity element. + fn mul_bits<'a, CS: ConstraintSystem>( + &self, + mut cs: CS, + result: &Self, + bits: impl Iterator, + ) -> Result { + let mut power = self.clone(); + let mut result = result.clone(); + for (i, bit) in bits.enumerate() { + let new_encoded = result.add(&mut cs.ns(|| format!("Add {}-th power", i)), &power)?; + result = Self::conditionally_select( + &mut cs.ns(|| format!("Select {}", i)), + bit.borrow(), + &new_encoded, + &result, + )?; + power.double_in_place(&mut cs.ns(|| format!("{}-th Doubling", i)))?; + } + Ok(result) + } + + fn precomputed_base_scalar_mul<'a, CS, I, B>( + &mut self, + mut cs: CS, + scalar_bits_with_base_powers: I, + ) -> Result<(), SynthesisError> + where + CS: ConstraintSystem, + I: Iterator, + B: Borrow, + G: 'a, + { + for (i, (bit, base_power)) in scalar_bits_with_base_powers.enumerate() { + let new_encoded = self.add_constant( + &mut cs.ns(|| format!("Add {}-th base power", i)), + &base_power, + )?; + *self = Self::conditionally_select( + &mut cs.ns(|| format!("Conditional Select {}", i)), + bit.borrow(), + &new_encoded, + &self, + )?; + } + Ok(()) + } + + fn precomputed_base_multiscalar_mul<'a, CS, T, I, B>( + mut cs: CS, + bases: &[B], + scalars: I, + ) -> Result + where + CS: ConstraintSystem, + T: 'a + ToBitsGadget + ?Sized, + I: Iterator, + B: Borrow<[G]>, + { + let mut result = Self::zero(&mut cs.ns(|| "Declare Result"))?; + // Compute ∏(h_i^{m_i}) for all i. + for (i, (bits, base_powers)) in scalars.zip(bases).enumerate() { + let base_powers = base_powers.borrow(); + let bits = bits.to_bits(&mut cs.ns(|| format!("Convert Scalar {} to bits", i)))?; + result.precomputed_base_scalar_mul( + cs.ns(|| format!("Chunk {}", i)), + bits.iter().zip(base_powers), + )?; + } + Ok(result) + } + + fn cost_of_add() -> usize; + + fn cost_of_double() -> usize; +} + +#[cfg(test)] +mod test { + use algebra::Field; + use r1cs_core::ConstraintSystem; + + use crate::{ + groups::GroupGadget, test_constraint_system::TestConstraintSystem, utils::AllocGadget, + }; + use algebra::groups::Group; + use rand; + + pub(crate) fn group_test< + ConstraintF: Field, + G: Group, + GG: GroupGadget, + CS: ConstraintSystem, + >( + cs: &mut CS, + a: GG, + b: GG, + ) { + let zero = GG::zero(cs.ns(|| "Zero")).unwrap(); + assert_eq!(zero, zero); + + // a == a + assert_eq!(a, a); + // a + 0 = a + assert_eq!(a.add(cs.ns(|| "a_plus_zero"), &zero).unwrap(), a); + // a - 0 = a + assert_eq!(a.sub(cs.ns(|| "a_minus_zero"), &zero).unwrap(), a); + // a - a = 0 + assert_eq!(a.sub(cs.ns(|| "a_minus_a"), &a).unwrap(), zero); + // 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); + // (a + b) + a = a + (b + a) + let ab_a = a_b.add(&mut cs.ns(|| "a_b_plus_a"), &a).unwrap(); + let a_ba = a.add(&mut cs.ns(|| "a_plus_b_a"), &b_a).unwrap(); + assert_eq!(ab_a, a_ba); + // a.double() = a + a + let a_a = a.add(cs.ns(|| "a + a"), &a).unwrap(); + let mut a2 = a.clone(); + a2.double_in_place(cs.ns(|| "2a")).unwrap(); + assert_eq!(a2, a_a); + // b.double() = b + b + let mut b2 = b.clone(); + b2.double_in_place(cs.ns(|| "2b")).unwrap(); + let b_b = b.add(cs.ns(|| "b + b"), &b).unwrap(); + assert_eq!(b2, b_b); + + let _ = a.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap(); + let _ = a.to_bytes_strict(&mut cs.ns(|| "ToBytes Strict")).unwrap(); + + let _ = b.to_bytes(&mut cs.ns(|| "b ToBytes")).unwrap(); + let _ = b + .to_bytes_strict(&mut cs.ns(|| "b ToBytes Strict")) + .unwrap(); + } + + #[test] + fn jubjub_group_gadgets_test() { + use crate::groups::jubjub::JubJubGadget; + use algebra::fields::jubjub::fq::Fq; + use algebra::curves::jubjub::JubJubProjective; + + let mut cs = TestConstraintSystem::::new(); + + let a: JubJubProjective = rand::random(); + let b: JubJubProjective = rand::random(); + + let a = JubJubGadget::alloc(&mut cs.ns(|| "generate_a"), || Ok(a)).unwrap(); + let b = JubJubGadget::alloc(&mut cs.ns(|| "generate_b"), || Ok(b)).unwrap(); + group_test::<_, JubJubProjective, _, _>(&mut cs.ns(|| "GroupTest(a, b)"), a, b); + } +} diff --git a/r1cs-std/src/lib.rs b/r1cs-std/src/lib.rs new file mode 100644 index 0000000..2e32ff5 --- /dev/null +++ b/r1cs-std/src/lib.rs @@ -0,0 +1,358 @@ +#![deny( + unused_import_braces, + unused_qualifications, + trivial_casts, + trivial_numeric_casts +)] +#![deny( + unused_qualifications, + variant_size_differences, + stable_features, + unreachable_pub +)] +#![deny( + non_shorthand_field_patterns, + unused_attributes, + unused_imports, + unused_extern_crates +)] +#![deny( + renamed_and_removed_lints, + stable_features, + unused_allocation, + unused_comparisons, + bare_trait_objects +)] +#![deny( + const_err, + unused_must_use, + unused_mut, + unused_unsafe, + private_in_public, + unsafe_code +)] +#![forbid(unsafe_code)] + +#[macro_use] +extern crate algebra; +#[macro_use] +extern crate derivative; + +use crate::ConstraintVar::*; +use algebra::Field; +use r1cs_core::{LinearCombination, SynthesisError, Variable}; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub}; + +pub mod test_constraint_system; + +pub mod bits; +pub use self::bits::*; + +pub mod fields; + +pub mod groups; + +pub mod pairing; + +pub mod eq; +pub mod select; +pub mod alloc; + +pub mod prelude { + pub use crate::eq::*; + pub use crate::select::*; + pub use crate::alloc::*; + pub use crate::fields::FieldGadget; + pub use crate::groups::GroupGadget; + pub use crate::bits::{ToBitsGadget, ToBytesGadget, boolean::Boolean, uint8::UInt8, uint32::UInt32}; +} + +pub trait Assignment { + fn get(self) -> Result; +} + +impl Assignment for Option { + fn get(self) -> Result { + match self { + Some(v) => Ok(v), + None => Err(SynthesisError::AssignmentMissing), + } + } +} + +#[derive(Clone, Debug)] +pub enum ConstraintVar { + LC(LinearCombination), + Var(Variable), +} + +impl From for ConstraintVar { + #[inline] + fn from(var: Variable) -> Self { + Var(var) + } +} + +impl From<(F, Variable)> for ConstraintVar { + #[inline] + fn from(coeff_var: (F, Variable)) -> Self { + LC(coeff_var.into()) + } +} + +impl From<(F, LinearCombination)> for ConstraintVar { + #[inline] + fn from((coeff, mut lc): (F, LinearCombination)) -> Self { + lc *= coeff; + LC(lc) + } +} + +impl From<(F, ConstraintVar)> for ConstraintVar { + #[inline] + fn from((coeff, var): (F, ConstraintVar)) -> Self { + match var { + LC(lc) => (coeff, lc).into(), + Var(var) => (coeff, var).into(), + } + } +} + +impl ConstraintVar { + #[inline] + pub fn zero() -> Self { + LC(LinearCombination::zero()) + } + + pub fn negate_in_place(&mut self) { + match self { + LC(ref mut lc) => lc.negate_in_place(), + Var(var) => *self = (-F::one(), *var).into(), + } + } + + pub fn double_in_place(&mut self) { + match self { + LC(lc) => lc.double_in_place(), + Var(var) => *self = (F::one().double(), *var).into(), + } + } +} + +impl Add> for ConstraintVar { + type Output = LinearCombination; + + #[inline] + fn add(self, other_lc: LinearCombination) -> LinearCombination { + match self { + LC(lc) => other_lc + lc, + Var(var) => other_lc + var, + } + } +} + +impl Sub> for ConstraintVar { + type Output = LinearCombination; + + #[inline] + fn sub(self, other_lc: LinearCombination) -> LinearCombination { + let result = match self { + LC(lc) => other_lc - lc, + Var(var) => other_lc - var, + }; + -result + } +} + +impl Add> for &ConstraintVar { + type Output = LinearCombination; + + #[inline] + fn add(self, other_lc: LinearCombination) -> LinearCombination { + match self { + LC(lc) => other_lc + lc, + Var(var) => other_lc + *var, + } + } +} + +impl Sub> for &ConstraintVar { + type Output = LinearCombination; + + #[inline] + fn sub(self, other_lc: LinearCombination) -> LinearCombination { + let result = match self { + LC(lc) => other_lc - lc, + Var(var) => other_lc - *var, + }; + -result + } +} + +impl Add<(F, Variable)> for ConstraintVar { + type Output = Self; + + #[inline] + fn add(self, var: (F, Variable)) -> Self { + let lc = match self { + LC(lc) => lc + var, + Var(var2) => LinearCombination::from(var2) + var, + }; + LC(lc) + } +} + +impl AddAssign<(F, Variable)> for ConstraintVar { + #[inline] + fn add_assign(&mut self, var: (F, Variable)) { + match self { + LC(ref mut lc) => *lc += var, + Var(var2) => *self = LC(LinearCombination::from(*var2) + var), + }; + } +} + +impl Neg for ConstraintVar { + type Output = Self; + + #[inline] + fn neg(mut self) -> Self { + self.negate_in_place(); + self + } +} + +impl Mul for ConstraintVar { + type Output = Self; + + #[inline] + fn mul(self, scalar: F) -> Self { + match self { + LC(lc) => LC(lc * scalar), + Var(var) => (scalar, var).into(), + } + } +} + +impl MulAssign for ConstraintVar { + #[inline] + fn mul_assign(&mut self, scalar: F) { + match self { + LC(lc) => *lc *= scalar, + Var(var) => *self = (scalar, *var).into(), + } + } +} + +impl Sub<(F, Variable)> for ConstraintVar { + type Output = Self; + + #[inline] + fn sub(self, (coeff, var): (F, Variable)) -> Self { + self + (-coeff, var) + } +} + +impl Add for ConstraintVar { + type Output = Self; + + fn add(self, other: Variable) -> Self { + self + (F::one(), other) + } +} + +impl Sub for ConstraintVar { + type Output = Self; + + #[inline] + fn sub(self, other: Variable) -> Self { + self - (F::one(), other) + } +} + +impl<'a, F: Field> Add<&'a Self> for ConstraintVar { + type Output = Self; + + #[inline] + fn add(self, other: &'a Self) -> Self { + let lc = match self { + LC(lc2) => lc2, + Var(var) => var.into(), + }; + let lc2 = match other { + LC(lc2) => lc + lc2, + Var(var) => lc + *var, + }; + LC(lc2) + } +} + +impl<'a, F: Field> Sub<&'a Self> for ConstraintVar { + type Output = Self; + + #[inline] + fn sub(self, other: &'a Self) -> Self { + let lc = match self { + LC(lc2) => lc2, + Var(var) => var.into(), + }; + let lc2 = match other { + LC(lc2) => lc - lc2, + Var(var) => lc - *var, + }; + LC(lc2) + } +} + +impl Add<&ConstraintVar> for &ConstraintVar { + type Output = ConstraintVar; + + #[inline] + fn add(self, other: &ConstraintVar) -> Self::Output { + (ConstraintVar::zero() + self) + other + } +} + +impl Sub<&ConstraintVar> for &ConstraintVar { + type Output = ConstraintVar; + + #[inline] + fn sub(self, other: &ConstraintVar) -> Self::Output { + (ConstraintVar::zero() + self) - other + } +} + +impl<'a, F: Field> Add<(F, &'a Self)> for ConstraintVar { + type Output = Self; + + #[inline] + fn add(self, (coeff, other): (F, &'a Self)) -> Self { + let mut lc = match self { + LC(lc2) => lc2, + Var(var) => LinearCombination::zero() + var, + }; + + lc = match other { + LC(lc2) => lc + (coeff, lc2), + Var(var) => lc + (coeff, *var), + }; + LC(lc) + } +} + +impl<'a, F: Field> Sub<(F, &'a Self)> for ConstraintVar { + type Output = Self; + + #[inline] + fn sub(self, (coeff, other): (F, &'a Self)) -> Self { + let mut lc = match self { + LC(lc2) => lc2, + Var(var) => LinearCombination::zero() + var, + }; + lc = match other { + LC(lc2) => lc - (coeff, lc2), + Var(var) => lc - (coeff, *var), + }; + LC(lc) + } +} diff --git a/r1cs-std/src/pairing/bls12/bls12_377.rs b/r1cs-std/src/pairing/bls12/bls12_377.rs new file mode 100644 index 0000000..490fbb0 --- /dev/null +++ b/r1cs-std/src/pairing/bls12/bls12_377.rs @@ -0,0 +1,4 @@ +use crate::pairing::bls12::PairingGadget as Bls12PG; +use algebra::curves::bls12_377::Bls12_377Parameters; + +pub type PairingGadget = Bls12PG; diff --git a/r1cs-std/src/pairing/bls12/bls12_381.rs b/r1cs-std/src/pairing/bls12/bls12_381.rs new file mode 100644 index 0000000..e69de29 diff --git a/r1cs-std/src/pairing/bls12/mod.rs b/r1cs-std/src/pairing/bls12/mod.rs new file mode 100644 index 0000000..e8e91d2 --- /dev/null +++ b/r1cs-std/src/pairing/bls12/mod.rs @@ -0,0 +1,206 @@ +use r1cs_core::{ConstraintSystem, SynthesisError}; + +use super::PairingGadget as PG; + +use crate::{ + fields::{fp::FpGadget, fp12::Fp12Gadget, fp2::Fp2Gadget, FieldGadget}, + groups::bls12::{G1Gadget, G1PreparedGadget, G2Gadget, G2PreparedGadget}, +}; +use algebra::{ + curves::{ + bls12::{ + Bls12, Bls12Parameters, G1Affine, G1Prepared, G1Projective, G2Affine, G2Prepared, + G2Projective, TwistType, + }, + models::ModelParameters, + PairingCurve, + }, + fields::{fp12_2over3over2::Fp12, BitIterator}, +}; +use std::marker::PhantomData; + +pub mod bls12_377; + +pub struct PairingGadget(PhantomData

); + +type Fp2G

= Fp2Gadget<

::Fp2Params,

::Fp>; + +impl PairingGadget

{ + // Evaluate the line function at point p. + fn ell>( + mut cs: CS, + f: &mut Fp12Gadget, + coeffs: &(Fp2G

, Fp2G

), + p: &G1Gadget

, + ) -> Result<(), SynthesisError> { + let zero = FpGadget::::zero(cs.ns(|| "fpg zero"))?; + + match P::TWIST_TYPE { + TwistType::M => { + let c0 = coeffs.0.clone(); + let mut c1 = coeffs.1.clone(); + let c2 = Fp2G::

::new(p.y.clone(), zero.clone()); + + c1.c0 = c1.c0.mul(cs.ns(|| "mul c1.c0"), &p.x)?; + c1.c1 = c1.c1.mul(cs.ns(|| "mul c1.c1"), &p.x)?; + *f = f.mul_by_014(cs.ns(|| "sparse mul f"), &c0, &c1, &c2)?; + Ok(()) + }, + TwistType::D => { + let c0 = Fp2G::

::new(p.y.clone(), zero.clone()); + let mut c1 = coeffs.0.clone(); + let c2 = coeffs.1.clone(); + + c1.c0 = c1.c0.mul(cs.ns(|| "mul c1.c0"), &p.x)?; + c1.c1 = c1.c1.mul(cs.ns(|| "mul c1.c1"), &p.x)?; + *f = f.mul_by_034(cs.ns(|| "sparse mul f"), &c0, &c1, &c2)?; + Ok(()) + }, + } + } + + fn exp_by_x>( + mut cs: CS, + f: &Fp12Gadget, + ) -> Result, SynthesisError> { + let mut result = f.cyclotomic_exp(cs.ns(|| "exp_by_x"), P::X)?; + if P::X_IS_NEGATIVE { + result.conjugate_in_place(cs.ns(|| "conjugate"))?; + } + Ok(result) + } +} + +impl PG, P::Fp> for PairingGadget

+where + G1Affine

: PairingCurve< + BaseField = ::BaseField, + ScalarField = ::ScalarField, + Projective = G1Projective

, + PairWith = G2Affine

, + Prepared = G1Prepared

, + PairingResult = Fp12, + >, + G2Affine

: PairingCurve< + BaseField = ::BaseField, + ScalarField = ::ScalarField, + Projective = G2Projective

, + PairWith = G1Affine

, + Prepared = G2Prepared

, + PairingResult = Fp12, + >, +{ + type G1Gadget = G1Gadget

; + type G2Gadget = G2Gadget

; + type G1PreparedGadget = G1PreparedGadget

; + type G2PreparedGadget = G2PreparedGadget

; + type GTGadget = Fp12Gadget; + + fn miller_loop>( + mut cs: CS, + ps: &[Self::G1PreparedGadget], + qs: &[Self::G2PreparedGadget], + ) -> Result { + let mut pairs = vec![]; + for (p, q) in ps.into_iter().zip(qs.into_iter()) { + pairs.push((p, q.ell_coeffs.iter())); + } + let mut f = Self::GTGadget::one(cs.ns(|| "one"))?; + + for (j, i) in BitIterator::new(P::X).skip(1).enumerate() { + let mut cs = cs.ns(|| format!("Iteration {}", j)); + f.square_in_place(cs.ns(|| "square"))?; + + for (k, &mut (p, ref mut coeffs)) in pairs.iter_mut().enumerate() { + let cs = cs.ns(|| format!("Double input {}", k)); + Self::ell(cs, &mut f, coeffs.next().unwrap(), &p.0)?; + } + + if i { + for (k, &mut (p, ref mut coeffs)) in pairs.iter_mut().enumerate() { + let cs = cs.ns(|| format!("Addition input {}", k)); + Self::ell(cs, &mut f, &coeffs.next().unwrap(), &p.0)?; + } + } + } + + if P::X_IS_NEGATIVE { + f.conjugate_in_place(cs.ns(|| "f conjugate"))?; + } + + Ok(f) + } + + fn final_exponentiation>( + mut cs: CS, + f: &Self::GTGadget, + ) -> Result { + // Computing the final exponentation following + // https://eprint.iacr.org/2016/130.pdf. + // We don't use their "faster" formula because it is difficult to make + // it work for curves with odd `P::X`. + // Hence we implement the slower algorithm from Table 1 below. + + let f1 = f.frobenius_map(cs.ns(|| "frobmap 1"), 6)?; + + f.inverse(cs.ns(|| "inverse 1")).and_then(|mut f2| { + // f2 = f^(-1); + // r = f^(p^6 - 1) + let mut r = f1; + r.mul_in_place(cs.ns(|| "r = f1 * f2"), &f2)?; + + // f2 = f^(p^6 - 1) + f2 = r.clone(); + // r = f^((p^6 - 1)(p^2)) + r.frobenius_map_in_place(cs.ns(|| "frobenius map 2"), 2)?; + + // r = f^((p^6 - 1)(p^2) + (p^6 - 1)) + // r = f^((p^6 - 1)(p^2 + 1)) + r.mul_in_place(cs.ns(|| "mul 0"), &f2)?; + + // Hard part of the final exponentation is below: + // From https://eprint.iacr.org/2016/130.pdf, Table 1 + let mut y0 = r.cyclotomic_square(cs.ns(|| "cyclotomic_sq 1"))?; + y0.conjugate_in_place(&mut cs.ns(|| "conjugate 2"))?; + + let mut y5 = Self::exp_by_x(&mut cs.ns(|| "exp_by_x 1"), &r)?; + + let mut y1 = y5.cyclotomic_square(&mut cs.ns(|| "square 1"))?; + let mut y3 = y0.mul(&mut cs.ns(|| "mul 1"), &y5)?; + y0 = Self::exp_by_x(cs.ns(|| "exp_by_x 2"), &y3)?; + let y2 = Self::exp_by_x(cs.ns(|| "exp_by_x 3"), &y0)?; + let mut y4 = Self::exp_by_x(cs.ns(|| "exp_by_x 4"), &y2)?; + y4.mul_in_place(cs.ns(|| "mul 2"), &y1)?; + y1 = Self::exp_by_x(cs.ns(|| "exp_by_x 5"), &y4)?; + y3.conjugate_in_place(cs.ns(|| "conjugate 3"))?; + y1.mul_in_place(cs.ns(|| "mul 3"), &y3)?; + y1.mul_in_place(cs.ns(|| "mul 4"), &r)?; + y3 = r.clone(); + y3.conjugate_in_place(cs.ns(|| "conjugate 4"))?; + y0.mul_in_place(cs.ns(|| "mul 5"), &r)?; + y0.frobenius_map_in_place(cs.ns(|| "frobmap 3"), 3)?; + y4.mul_in_place(cs.ns(|| "mul 6"), &y3)?; + y4.frobenius_map_in_place(cs.ns(|| "frobmap 4"), 1)?; + y5.mul_in_place(cs.ns(|| "mul 7"), &y2)?; + y5.frobenius_map_in_place(cs.ns(|| "frobmap 5"), 2)?; + y5.mul_in_place(cs.ns(|| "mul 8"), &y0)?; + y5.mul_in_place(cs.ns(|| "mul 9"), &y4)?; + y5.mul_in_place(cs.ns(|| "mul 10"), &y1)?; + Ok(y5) + }) + } + + fn prepare_g1>( + cs: CS, + p: &Self::G1Gadget, + ) -> Result { + Self::G1PreparedGadget::from_affine(cs, p) + } + + fn prepare_g2>( + cs: CS, + q: &Self::G2Gadget, + ) -> Result { + Self::G2PreparedGadget::from_affine(cs, q) + } +} diff --git a/r1cs-std/src/pairing/mod.rs b/r1cs-std/src/pairing/mod.rs new file mode 100644 index 0000000..17da3f6 --- /dev/null +++ b/r1cs-std/src/pairing/mod.rs @@ -0,0 +1,178 @@ +use crate::prelude::*; +use algebra::{Field, PairingEngine}; +use r1cs_core::{ConstraintSystem, SynthesisError}; +use std::fmt::Debug; + +pub mod bls12; +pub use self::bls12::bls12_377; + +pub trait PairingGadget { + type G1Gadget: GroupGadget; + type G2Gadget: GroupGadget; + type G1PreparedGadget: ToBytesGadget + Clone + Debug; + type G2PreparedGadget: ToBytesGadget + Clone + Debug; + type GTGadget: FieldGadget + Clone; + + fn miller_loop>( + cs: CS, + p: &[Self::G1PreparedGadget], + q: &[Self::G2PreparedGadget], + ) -> Result; + + fn final_exponentiation>( + cs: CS, + p: &Self::GTGadget, + ) -> Result; + + fn pairing>( + mut cs: CS, + p: Self::G1PreparedGadget, + q: Self::G2PreparedGadget, + ) -> Result { + let tmp = Self::miller_loop(cs.ns(|| "miller loop"), &[p], &[q])?; + Self::final_exponentiation(cs.ns(|| "final_exp"), &tmp) + } + + /// Computes a product of pairings. + #[must_use] + fn product_of_pairings>( + mut cs: CS, + p: &[Self::G1PreparedGadget], + q: &[Self::G2PreparedGadget], + ) -> Result { + let miller_result = Self::miller_loop(&mut cs.ns(|| "Miller loop"), p, q)?; + Self::final_exponentiation(&mut cs.ns(|| "Final Exp"), &miller_result) + } + + fn prepare_g1>( + cs: CS, + q: &Self::G1Gadget, + ) -> Result; + + fn prepare_g2>( + cs: CS, + q: &Self::G2Gadget, + ) -> Result; +} + +#[cfg(test)] +mod test { + // use rand; + use crate::{boolean::Boolean, test_constraint_system::TestConstraintSystem}; + use algebra::{BitIterator, Field}; + use r1cs_core::ConstraintSystem; + + #[test] + fn bls12_377_gadget_bilinearity_test() { + use algebra::{ + fields::{bls12_377::{fr::Fr, fq::Fq}, PrimeField}, + PairingEngine, ProjectiveCurve, + }; + + use super::bls12_377::PairingGadget; + use crate::{ + fields::FieldGadget, + groups::bls12::bls12_377::{G1Gadget, G1PreparedGadget, G2Gadget, G2PreparedGadget}, + pairing::PairingGadget as _, + utils::{AllocGadget, EqGadget}, + }; + use algebra::curves::bls12_377::{Bls12_377, G1Projective, G2Projective}; + use std::ops::Mul; + + let mut cs = TestConstraintSystem::::new(); + + // let a: G1Projective = rand::random(); + // let b: G2Projective = rand::random(); + // let s: Fr = rand::random(); + + let a: G1Projective = G1Projective::prime_subgroup_generator(); + let b: G2Projective = G2Projective::prime_subgroup_generator(); + let s: Fr = Fr::one() + &Fr::one(); + + let sa = a.mul(&s); + let sb = b.mul(&s); + + let a_g = G1Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap(); + let b_g = G2Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap(); + let sa_g = G1Gadget::alloc(&mut cs.ns(|| "sa"), || Ok(sa)).unwrap(); + let sb_g = G2Gadget::alloc(&mut cs.ns(|| "sb"), || Ok(sb)).unwrap(); + + let a_prep_g = G1PreparedGadget::from_affine(&mut cs.ns(|| "a_prep"), &a_g).unwrap(); + let b_prep_g = G2PreparedGadget::from_affine(&mut cs.ns(|| "b_prep"), &b_g).unwrap(); + + let sa_prep_g = G1PreparedGadget::from_affine(&mut cs.ns(|| "sa_prep"), &sa_g).unwrap(); + let sb_prep_g = G2PreparedGadget::from_affine(&mut cs.ns(|| "sb_prep"), &sb_g).unwrap(); + + let (ans1_g, ans1_n) = { + let ans_g = PairingGadget::pairing( + cs.ns(|| "pair(sa, b)"), + sa_prep_g.clone(), + b_prep_g.clone(), + ) + .unwrap(); + let ans_n = Bls12_377::pairing(sa, b); + (ans_g, ans_n) + }; + + let (ans2_g, ans2_n) = { + let ans_g = PairingGadget::pairing( + cs.ns(|| "pair(a, sb)"), + a_prep_g.clone(), + sb_prep_g.clone(), + ) + .unwrap(); + let ans_n = Bls12_377::pairing(a, sb); + (ans_g, ans_n) + }; + + let (ans3_g, ans3_n) = { + let s_iter = BitIterator::new(s.into_repr()) + .map(|bit| Boolean::constant(bit)) + .collect::>(); + + let mut ans_g = + PairingGadget::pairing(cs.ns(|| "pair(a, b)"), a_prep_g.clone(), b_prep_g.clone()) + .unwrap(); + let mut ans_n = Bls12_377::pairing(a, b); + ans_n = ans_n.pow(s.into_repr()); + ans_g = ans_g.pow(cs.ns(|| "pow"), &s_iter).unwrap(); + + (ans_g, ans_n) + }; + + assert_eq!(ans1_n, ans2_n, "Failed ans1_native == ans2_native"); + assert_eq!(ans2_n, ans3_n, "Failed ans2_native == ans3_native"); + assert_eq!( + ans1_g.get_value(), + ans3_g.get_value(), + "Failed ans1 == ans3" + ); + assert_eq!( + ans1_g.get_value(), + ans2_g.get_value(), + "Failed ans1 == ans2" + ); + assert_eq!( + ans2_g.get_value(), + ans3_g.get_value(), + "Failed ans2 == ans3" + ); + + ans1_g + .enforce_equal(&mut cs.ns(|| "ans1 == ans2?"), &ans2_g) + .unwrap(); + ans2_g + .enforce_equal(&mut cs.ns(|| "ans2 == ans3?"), &ans3_g) + .unwrap(); + + assert_eq!(ans1_g.get_value().unwrap(), ans1_n, "Failed native test 1"); + assert_eq!(ans2_g.get_value().unwrap(), ans2_n, "Failed native test 2"); + assert_eq!(ans3_g.get_value().unwrap(), ans3_n, "Failed native test 3"); + + if !cs.is_satisfied() { + println!("Unsatisfied: {:?}", cs.which_is_unsatisfied()); + } + + assert!(cs.is_satisfied(), "cs is not satisfied"); + } +} diff --git a/r1cs-std/src/select.rs b/r1cs-std/src/select.rs new file mode 100644 index 0000000..e2828c4 --- /dev/null +++ b/r1cs-std/src/select.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; +use r1cs_core::{ConstraintSystem, SynthesisError}; +use algebra::Field; + + +/// If condition is `true`, return `first`; else, select `second`. +pub trait CondSelectGadget +where + Self: Sized, +{ + fn conditionally_select>( + cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result; + + fn cost() -> usize; +} + +/// Uses two bits to perform a lookup into a table +pub trait TwoBitLookupGadget +where + Self: Sized, +{ + type TableConstant; + fn two_bit_lookup>( + cs: CS, + bits: &[Boolean], + constants: &[Self::TableConstant], + ) -> Result; + + fn cost() -> usize; +} + + diff --git a/r1cs-std/src/test_constraint_system.rs b/r1cs-std/src/test_constraint_system.rs new file mode 100644 index 0000000..8096c4b --- /dev/null +++ b/r1cs-std/src/test_constraint_system.rs @@ -0,0 +1,230 @@ +use algebra::Field; +use r1cs_core::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; + +use radix_trie::Trie; + +#[derive(Debug)] +enum NamedObject { + Constraint(usize), + Var(Variable), + Namespace, +} + +/// Constraint system for testing purposes. +pub struct TestConstraintSystem { + named_objects: Trie, + current_namespace: Vec, + pub constraints: Vec<( + LinearCombination, + LinearCombination, + LinearCombination, + String, + )>, + inputs: Vec<(ConstraintF, String)>, + aux: Vec<(ConstraintF, String)>, +} + +impl TestConstraintSystem { + fn eval_lc( + terms: &[(Variable, ConstraintF)], + inputs: &[(ConstraintF, String)], + aux: &[(ConstraintF, String)], + ) -> ConstraintF { + let mut acc = ConstraintF::zero(); + + for &(var, ref coeff) in terms { + let mut tmp = match var.get_unchecked() { + Index::Input(index) => inputs[index].0, + Index::Aux(index) => aux[index].0, + }; + + tmp.mul_assign(&coeff); + acc.add_assign(&tmp); + } + + acc + } +} + +impl TestConstraintSystem { + pub fn new() -> TestConstraintSystem { + let mut map = Trie::new(); + map.insert( + "ONE".into(), + NamedObject::Var(TestConstraintSystem::::one()), + ); + + TestConstraintSystem { + named_objects: map, + current_namespace: vec![], + constraints: vec![], + inputs: vec![(ConstraintF::one(), "ONE".into())], + aux: vec![], + } + } + + pub fn print_named_objects(&self) { + for &(_, _, _, ref name) in &self.constraints { + println!("{}", name); + } + } + + pub fn which_is_unsatisfied(&self) -> Option<&str> { + for &(ref a, ref b, ref c, ref path) in &self.constraints { + let mut a = Self::eval_lc(a.as_ref(), &self.inputs, &self.aux); + let b = Self::eval_lc(b.as_ref(), &self.inputs, &self.aux); + let c = Self::eval_lc(c.as_ref(), &self.inputs, &self.aux); + + a.mul_assign(&b); + + if a != c { + return Some(&*path); + } + } + + None + } + + pub fn is_satisfied(&self) -> bool { + self.which_is_unsatisfied().is_none() + } + + pub fn num_constraints(&self) -> usize { + self.constraints.len() + } + + pub fn set(&mut self, path: &str, to: ConstraintF) { + match self.named_objects.get(path) { + Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { + Index::Input(index) => self.inputs[index].0 = to, + Index::Aux(index) => self.aux[index].0 = to, + }, + Some(e) => panic!( + "tried to set path `{}` to value, but `{:?}` already exists there.", + path, e + ), + _ => panic!("no variable exists at path: {}", path), + } + } + + pub fn get(&mut self, path: &str) -> ConstraintF { + match self.named_objects.get(path) { + Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { + Index::Input(index) => self.inputs[index].0, + Index::Aux(index) => self.aux[index].0, + }, + Some(e) => panic!( + "tried to get value of path `{}`, but `{:?}` exists there (not a variable)", + path, e + ), + _ => panic!("no variable exists at path: {}", path), + } + } + + fn set_named_obj(&mut self, path: String, to: NamedObject) { + if self.named_objects.get(&path).is_some() { + panic!("tried to create object at existing path: {}", path); + } + + self.named_objects.insert(path, to); + } +} + +fn compute_path(ns: &[String], this: String) -> String { + if this.chars().any(|a| a == '/') { + panic!("'/' is not allowed in names"); + } + + let mut name = String::new(); + + let mut needs_separation = false; + for ns in ns.iter().chain(Some(&this).into_iter()) { + if needs_separation { + name += "/"; + } + + name += ns; + needs_separation = true; + } + + name +} + +impl ConstraintSystem for TestConstraintSystem { + type Root = Self; + + fn alloc(&mut self, annotation: A, f: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + let index = self.aux.len(); + let path = compute_path(&self.current_namespace, annotation().into()); + self.aux.push((f()?, path.clone())); + let var = Variable::new_unchecked(Index::Aux(index)); + self.set_named_obj(path, NamedObject::Var(var)); + + Ok(var) + } + + fn alloc_input(&mut self, annotation: A, f: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + let index = self.inputs.len(); + let path = compute_path(&self.current_namespace, annotation().into()); + self.inputs.push((f()?, path.clone())); + let var = Variable::new_unchecked(Index::Input(index)); + self.set_named_obj(path, NamedObject::Var(var)); + + Ok(var) + } + + fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) + where + A: FnOnce() -> AR, + AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, + { + let path = compute_path(&self.current_namespace, annotation().into()); + let index = self.constraints.len(); + self.set_named_obj(path.clone(), NamedObject::Constraint(index)); + + let mut a = a(LinearCombination::zero()); + let mut b = b(LinearCombination::zero()); + let mut c = c(LinearCombination::zero()); + a.0.shrink_to_fit(); + b.0.shrink_to_fit(); + c.0.shrink_to_fit(); + + self.constraints.push((a, b, c, path)); + } + + fn push_namespace(&mut self, name_fn: N) + where + NR: Into, + N: FnOnce() -> NR, + { + let name = name_fn().into(); + let path = compute_path(&self.current_namespace, name.clone()); + self.set_named_obj(path.clone(), NamedObject::Namespace); + self.current_namespace.push(name); + } + + fn pop_namespace(&mut self) { + assert!(self.current_namespace.pop().is_some()); + } + + fn get_root(&mut self) -> &mut Self::Root { + self + } + + fn num_constraints(&self) -> usize { + self.constraints.len() + } +}