adds a smaller than gadget
Kobi Gurkan kobigurk@gmail.com
Tue, 17 Mar 2020 00:43:15 +0200
2 files changed,
251 insertions(+),
0 deletions(-)
M
r1cs-std/src/lib.rs
→
r1cs-std/src/lib.rs
@@ -62,6 +62,7 @@
pub mod alloc; pub mod eq; pub mod select; +pub mod smaller_than; pub mod prelude { pub use crate::{
A
r1cs-std/src/smaller_than.rs
@@ -0,0 +1,250 @@
+use crate::{ + boolean::Boolean, + fields::{fp::FpGadget, FieldGadget}, + ToBitsGadget, +}; +use algebra::PrimeField; +use r1cs_core::{ConstraintSystem, SynthesisError}; +use std::marker::PhantomData; + +pub struct SmallerThanGadget<ConstraintF: PrimeField> { + constraint_field_type: PhantomData<ConstraintF>, +} + +impl<ConstraintF: PrimeField> SmallerThanGadget<ConstraintF> { + // the function assumes a and b are known to be <= (p-1)/2 + pub fn is_smaller_than<CS: ConstraintSystem<ConstraintF>>( + mut cs: CS, + a: &FpGadget<ConstraintF>, + b: &FpGadget<ConstraintF>, + ) -> Result<Boolean, SynthesisError> { + 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 is_smaller_than_or_equal_to<CS: ConstraintSystem<ConstraintF>>( + mut cs: CS, + a: &FpGadget<ConstraintF>, + b: &FpGadget<ConstraintF>, + ) -> Result<Boolean, SynthesisError> { + 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) + } + + // the function assumes a and b are known to be <= (p-1)/2 + pub fn enforce_smaller_than<CS: ConstraintSystem<ConstraintF>>( + mut cs: CS, + a: &FpGadget<ConstraintF>, + b: &FpGadget<ConstraintF>, + ) -> 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()), + ); + + Ok(()) + } + + // the function assumes a and b are known to be <= (p-1)/2 + pub fn enforce_smaller_than_or_equal_to<CS: ConstraintSystem<ConstraintF>>( + mut cs: CS, + a: &FpGadget<ConstraintF>, + b: &FpGadget<ConstraintF>, + ) -> 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) + } + + pub fn enforce_smaller_than_strict<CS: ConstraintSystem<ConstraintF>>( + mut cs: CS, + a: &FpGadget<ConstraintF>, + b: &FpGadget<ConstraintF>, + ) -> 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)?; + 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()), + ); + + Ok(()) + } + + pub fn enforce_smaller_than_or_equal_to_strict<CS: ConstraintSystem<ConstraintF>>( + mut cs: CS, + a: &FpGadget<ConstraintF>, + b: &FpGadget<ConstraintF>, + ) -> 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)] +mod test { + use rand::{Rng, SeedableRng}; + use rand_xorshift::XorShiftRng; + use std::cmp::Ordering; + + use super::SmallerThanGadget; + use crate::{ + alloc::AllocGadget, fields::fp::FpGadget, test_constraint_system::TestConstraintSystem, + }; + use algebra::{bls12_381::Fr, PrimeField, UniformRand}; + use r1cs_core::ConstraintSystem; + + #[test] + fn test_smaller_than() { + let mut rng = &mut XorShiftRng::from_seed([ + 0x5d, 0xbe, 0x62, 0x59, 0x8d, 0x31, 0x3d, 0x76, 0x32, 0x37, 0xdb, 0x17, 0xe5, 0xbc, + 0x06, 0x54, + ]); + fn rand_in_range<R: Rng>(rng: &mut R) -> Fr { + let pminusonedivtwo = Fr::from_repr(Fr::modulus_minus_one_div_two()); + let mut r; + loop { + r = Fr::rand(rng); + if r <= pminusonedivtwo { + break; + } + } + r + } + for i in 0..10 { + let mut cs = TestConstraintSystem::<Fr>::new(); + let a = rand_in_range(&mut rng); + let a_var = FpGadget::<Fr>::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); + let b = rand_in_range(&mut rng); + let b_var = FpGadget::<Fr>::alloc(cs.ns(|| "b"), || Ok(b)).unwrap(); + + match a.cmp(&b) { + Ordering::Less => { + SmallerThanGadget::<Fr>::enforce_smaller_than_strict( + cs.ns(|| "smaller than test"), + &a_var, + &b_var, + ) + .unwrap(); + SmallerThanGadget::<Fr>::enforce_smaller_than_or_equal_to_strict( + cs.ns(|| "smaller than test 2"), + &a_var, + &b_var, + ) + .unwrap(); + }, + Ordering::Greater => { + SmallerThanGadget::<Fr>::enforce_smaller_than_strict( + cs.ns(|| "smaller than test"), + &b_var, + &a_var, + ) + .unwrap(); + SmallerThanGadget::<Fr>::enforce_smaller_than_or_equal_to_strict( + cs.ns(|| "smaller than test 2"), + &b_var, + &a_var, + ) + .unwrap(); + }, + _ => {}, + } + + if i == 0 { + println!("number of constraints: {}", cs.num_constraints()); + } + assert!(cs.is_satisfied()); + } + + for _i in 0..10 { + let mut cs = TestConstraintSystem::<Fr>::new(); + let a = rand_in_range(&mut rng); + let a_var = FpGadget::<Fr>::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); + let b = rand_in_range(&mut rng); + let b_var = FpGadget::<Fr>::alloc(cs.ns(|| "b"), || Ok(b)).unwrap(); + + match b.cmp(&a) { + Ordering::Less => { + SmallerThanGadget::<Fr>::enforce_smaller_than_strict( + cs.ns(|| "smaller than test"), + &a_var, + &b_var, + ) + .unwrap(); + SmallerThanGadget::<Fr>::enforce_smaller_than_or_equal_to_strict( + cs.ns(|| "smaller than test 2"), + &a_var, + &b_var, + ) + .unwrap(); + }, + Ordering::Greater => { + SmallerThanGadget::<Fr>::enforce_smaller_than_strict( + cs.ns(|| "smaller than test"), + &b_var, + &a_var, + ) + .unwrap(); + SmallerThanGadget::<Fr>::enforce_smaller_than_or_equal_to( + cs.ns(|| "smaller than test 2"), + &b_var, + &a_var, + ) + .unwrap(); + }, + _ => {}, + } + + assert!(!cs.is_satisfied()); + } + + for _i in 0..10 { + let mut cs = TestConstraintSystem::<Fr>::new(); + let a = rand_in_range(&mut rng); + let a_var = FpGadget::<Fr>::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); + SmallerThanGadget::<Fr>::enforce_smaller_than_strict( + cs.ns(|| "smaller than test"), + &a_var, + &a_var, + ) + .unwrap(); + + assert!(!cs.is_satisfied()); + } + + for _i in 0..10 { + let mut cs = TestConstraintSystem::<Fr>::new(); + let a = rand_in_range(&mut rng); + let a_var = FpGadget::<Fr>::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); + SmallerThanGadget::<Fr>::enforce_smaller_than_or_equal_to( + cs.ns(|| "smaller than or equal to test"), + &a_var, + &a_var, + ) + .unwrap(); + + assert!(cs.is_satisfied()); + } + } +}