From 7ed38fe4a698f66f5c259cfc2069b364fbceee92 Mon Sep 17 00:00:00 2001 From: Kobi Gurkan Date: Mon, 23 Mar 2020 21:47:11 +0200 Subject: [PATCH] makes API cmp-like --- r1cs-std/src/{smaller_than.rs => cmp.rs} | 210 +++++++++++++++-------- r1cs-std/src/lib.rs | 3 +- 2 files changed, 143 insertions(+), 70 deletions(-) rename r1cs-std/src/{smaller_than.rs => cmp.rs} (53%) diff --git a/r1cs-std/src/smaller_than.rs b/r1cs-std/src/cmp.rs similarity index 53% rename from r1cs-std/src/smaller_than.rs rename to r1cs-std/src/cmp.rs index a3669dc..c7a660b 100644 --- a/r1cs-std/src/smaller_than.rs +++ b/r1cs-std/src/cmp.rs @@ -5,83 +5,144 @@ use crate::{ }; use algebra::PrimeField; use r1cs_core::{ConstraintSystem, SynthesisError}; -use std::marker::PhantomData; +use std::{ + cmp::Ordering, + marker::PhantomData +}; -pub struct SmallerThanGadget { +pub struct CmpGadget { constraint_field_type: PhantomData, } -impl SmallerThanGadget { - // the function assumes a and b are known to be <= (p-1)/2 - pub fn is_smaller_than>( +impl CmpGadget { + fn process_cmp_inputs>( + mut cs: CS, + a: &FpGadget, + b: &FpGadget, + ordering: Ordering, + should_also_check_equality: bool, + ) -> Result<(FpGadget, FpGadget), SynthesisError> { + let left; + let right; + match ordering { + Ordering::Less => { left = a; right = b; } + Ordering::Greater => {left = b; right = a; } + Ordering::Equal => { return Err(SynthesisError::Unsatisfiable); } + }; + let right_for_check = if should_also_check_equality { + right.add_constant(cs.ns(|| "plus one"), &ConstraintF::one())? + } else { + right.clone() + }; + + Ok((left.clone(), right_for_check)) + } + + fn check_smaller_than_mod_minus_one_div_two>( + mut cs: CS, + a: &FpGadget, + ) -> Result<(), SynthesisError> { + let a_bits = a.to_bits(cs.ns(|| "a to bits"))?; + Boolean::enforce_smaller_or_equal_than::<_, _, ConstraintF, _>( + cs.ns(|| "enforce smaller than modulus minus one div two"), + &a_bits, + ConstraintF::modulus_minus_one_div_two(), + )?; + + Ok(()) + } + + /// this function verifies a and b are <= (p-1)/2 + pub fn enforce_cmp>( + mut cs: CS, + a: &FpGadget, + b: &FpGadget, + ordering: Ordering, + should_also_check_equality: bool, + ) -> Result<(), SynthesisError> { + let (left, right) = Self::process_cmp_inputs(cs.ns(|| "process cmp inputs"), a, b, ordering, should_also_check_equality)?; + Self::enforce_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), &left, &right) + } + + /// this function assumes a and b are known to be <= (p-1)/2 + pub fn enforce_cmp_unchecked>( + mut cs: CS, + a: &FpGadget, + b: &FpGadget, + ordering: Ordering, + should_also_check_equality: bool, + ) -> Result<(), SynthesisError> { + let (left, right) = Self::process_cmp_inputs(cs.ns(|| "process cmp inputs"), a, b, ordering, should_also_check_equality)?; + Self::enforce_smaller_than(cs.ns(|| "enforce smaller than"), &left, &right) + } + + /// this function verifies a and b are <= (p-1)/2 + pub fn is_cmp>( mut cs: CS, a: &FpGadget, b: &FpGadget, + ordering: Ordering, + should_also_check_equality: bool, ) -> Result { - let two = ConstraintF::one() + ConstraintF::one(); - let d0 = a.sub(cs.ns(|| "a - b"), b)?; - let d = d0.mul_by_constant(cs.ns(|| "mul 2"), &two)?; - let d_bits = d.to_bits_strict(cs.ns(|| "d to bits"))?; - let d_bits_len = d_bits.len(); - Ok(d_bits[d_bits_len - 1]) + let (left, right) = Self::process_cmp_inputs(cs.ns(|| "process cmp inputs"), a, b, ordering, should_also_check_equality)?; + Self::is_smaller_than(cs.ns(|| "enforce smaller than"), &left, &right) } - // the function assumes a and b are known to be <= (p-1)/2 - pub fn is_smaller_than_or_equal_to>( + /// this function assumes a and b are known to be <= (p-1)/2 + pub fn is_cmp_unchecked>( mut cs: CS, a: &FpGadget, b: &FpGadget, + ordering: Ordering, + should_also_check_equality: bool, ) -> Result { - let b_plus_one = b.add_constant(cs.ns(|| "plus one"), &ConstraintF::one())?; - Self::is_smaller_than(cs.ns(|| "is smaller than"), a, &b_plus_one) + let (left, right) = Self::process_cmp_inputs(cs.ns(|| "process cmp inputs"), a, b, ordering, should_also_check_equality)?; + Self::is_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), &left, &right) } - // the function assumes a and b are known to be <= (p-1)/2 - pub fn enforce_smaller_than>( + /// this function verifies a and b are <= (p-1)/2 + fn is_smaller_than>( mut cs: CS, a: &FpGadget, b: &FpGadget, - ) -> Result<(), SynthesisError> { - let is_smaller_than = Self::is_smaller_than(cs.ns(|| "is smaller than"), a, b)?; - cs.enforce( - || "enforce smaller than", - |_| is_smaller_than.lc(CS::one(), ConstraintF::one()), - |lc| lc + (ConstraintF::one(), CS::one()), - |lc| lc + (ConstraintF::one(), CS::one()), - ); + ) -> Result { + Self::check_smaller_than_mod_minus_one_div_two(cs.ns(|| "check a in range"), a)?; + Self::check_smaller_than_mod_minus_one_div_two(cs.ns(|| "check b in range"), b)?; + Self::is_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), a, b) + } - Ok(()) + /// this function assumes a and b are known to be <= (p-1)/2 + fn is_smaller_than_unchecked>( + mut cs: CS, + a: &FpGadget, + b: &FpGadget, + ) -> Result { + let two = ConstraintF::one() + ConstraintF::one(); + let d0 = a.sub(cs.ns(|| "a - b"), b)?; + let d = d0.mul_by_constant(cs.ns(|| "mul 2"), &two)?; + let d_bits = d.to_bits_strict(cs.ns(|| "d to bits"))?; + let d_bits_len = d_bits.len(); + Ok(d_bits[d_bits_len - 1]) } - // the function assumes a and b are known to be <= (p-1)/2 - pub fn enforce_smaller_than_or_equal_to>( + /// this function verifies a and b are <= (p-1)/2 + fn enforce_smaller_than>( mut cs: CS, a: &FpGadget, b: &FpGadget, ) -> Result<(), SynthesisError> { - let b_plus_one = b.add_constant(cs.ns(|| "plus one"), &ConstraintF::one())?; - Self::enforce_smaller_than(cs.ns(|| "enforce smaller than"), a, &b_plus_one) + Self::check_smaller_than_mod_minus_one_div_two(cs.ns(|| "check a in range"), a)?; + Self::check_smaller_than_mod_minus_one_div_two(cs.ns(|| "check b in range"), b)?; + Self::enforce_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), a, b) } - pub fn enforce_smaller_than_strict>( + /// this function assumes a and b are known to be <= (p-1)/2 + fn enforce_smaller_than_unchecked>( mut cs: CS, a: &FpGadget, b: &FpGadget, ) -> Result<(), SynthesisError> { - let a_bits = a.to_bits(cs.ns(|| "a to bits"))?; - Boolean::enforce_smaller_or_equal_than::<_, _, ConstraintF, _>( - cs.ns(|| "enforce a smaller than modulus minus one div two"), - &a_bits, - ConstraintF::modulus_minus_one_div_two(), - )?; - let b_bits = b.to_bits(cs.ns(|| "b to bits"))?; - Boolean::enforce_smaller_or_equal_than::<_, _, ConstraintF, _>( - cs.ns(|| "enforce b smaller than modulus minus one div two"), - &b_bits, - ConstraintF::modulus_minus_one_div_two(), - )?; - - let is_smaller_than = Self::is_smaller_than(cs.ns(|| "is smaller than"), a, b)?; + let is_smaller_than = Self::is_smaller_than_unchecked(cs.ns(|| "is smaller than"), a, b)?; cs.enforce( || "enforce smaller than", |_| is_smaller_than.lc(CS::one(), ConstraintF::one()), @@ -91,15 +152,6 @@ impl SmallerThanGadget { Ok(()) } - - pub fn enforce_smaller_than_or_equal_to_strict>( - mut cs: CS, - a: &FpGadget, - b: &FpGadget, - ) -> Result<(), SynthesisError> { - let b_plus_one = b.add_constant(cs.ns(|| "plus one"), &ConstraintF::one())?; - Self::enforce_smaller_than_strict(cs.ns(|| "enforce smaller than strict"), a, &b_plus_one) - } } #[cfg(test)] @@ -108,7 +160,7 @@ mod test { use rand_xorshift::XorShiftRng; use std::cmp::Ordering; - use super::SmallerThanGadget; + use super::CmpGadget; use crate::{ alloc::AllocGadget, fields::fp::FpGadget, test_constraint_system::TestConstraintSystem, }; @@ -141,30 +193,38 @@ mod test { match a.cmp(&b) { Ordering::Less => { - SmallerThanGadget::::enforce_smaller_than_strict( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test"), &a_var, &b_var, + Ordering::Less, + false, ) .unwrap(); - SmallerThanGadget::::enforce_smaller_than_or_equal_to_strict( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test 2"), &a_var, &b_var, + Ordering::Less, + true, ) .unwrap(); }, Ordering::Greater => { - SmallerThanGadget::::enforce_smaller_than_strict( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test"), - &b_var, &a_var, + &b_var, + Ordering::Greater, + false, ) .unwrap(); - SmallerThanGadget::::enforce_smaller_than_or_equal_to_strict( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test 2"), - &b_var, &a_var, + &b_var, + Ordering::Greater, + true, ) .unwrap(); }, @@ -186,30 +246,38 @@ mod test { match b.cmp(&a) { Ordering::Less => { - SmallerThanGadget::::enforce_smaller_than_strict( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test"), &a_var, &b_var, + Ordering::Less, + false, ) .unwrap(); - SmallerThanGadget::::enforce_smaller_than_or_equal_to_strict( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test 2"), &a_var, &b_var, + Ordering::Less, + true, ) .unwrap(); }, Ordering::Greater => { - SmallerThanGadget::::enforce_smaller_than_strict( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test"), - &b_var, &a_var, + &b_var, + Ordering::Greater, + false, ) .unwrap(); - SmallerThanGadget::::enforce_smaller_than_or_equal_to( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test 2"), - &b_var, &a_var, + &b_var, + Ordering::Greater, + true, ) .unwrap(); }, @@ -223,10 +291,12 @@ mod test { let mut cs = TestConstraintSystem::::new(); let a = rand_in_range(&mut rng); let a_var = FpGadget::::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); - SmallerThanGadget::::enforce_smaller_than_strict( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than test"), &a_var, &a_var, + Ordering::Less, + false, ) .unwrap(); @@ -237,10 +307,12 @@ mod test { let mut cs = TestConstraintSystem::::new(); let a = rand_in_range(&mut rng); let a_var = FpGadget::::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); - SmallerThanGadget::::enforce_smaller_than_or_equal_to( + CmpGadget::::enforce_cmp( cs.ns(|| "smaller than or equal to test"), &a_var, &a_var, + Ordering::Less, + true, ) .unwrap(); diff --git a/r1cs-std/src/lib.rs b/r1cs-std/src/lib.rs index 39ea362..399fd0a 100644 --- a/r1cs-std/src/lib.rs +++ b/r1cs-std/src/lib.rs @@ -62,7 +62,7 @@ pub mod pairing; pub mod alloc; pub mod eq; pub mod select; -pub mod smaller_than; +pub mod cmp; pub mod prelude { pub use crate::{ @@ -74,6 +74,7 @@ pub mod prelude { instantiated::*, pairing::PairingGadget, select::*, + cmp::CmpGadget, }; }