diff --git a/src/circuit/boolean.rs b/src/circuit/boolean.rs index c1cfd2d..8a165e5 100644 --- a/src/circuit/boolean.rs +++ b/src/circuit/boolean.rs @@ -435,6 +435,9 @@ impl Boolean { current_run.truncate(0); } + // TODO: this could be optimized with a k-ary operation + // (all zeros are required in the run if last_run is zero) + // If `last_run` is true, `a` must be false, or it would // not be in the field. // diff --git a/src/circuit/num.rs b/src/circuit/num.rs index 27858b4..2c244e3 100644 --- a/src/circuit/num.rs +++ b/src/circuit/num.rs @@ -1,7 +1,8 @@ use pairing::{ Engine, Field, - PrimeField + PrimeField, + BitIterator }; use bellman::{ @@ -15,7 +16,8 @@ use super::{ }; use super::boolean::{ - Boolean + Boolean, + AllocatedBit }; pub struct AllocatedNum { @@ -46,6 +48,67 @@ impl AllocatedNum { }) } + pub fn into_bits( + &self, + mut cs: CS + ) -> Result>, SynthesisError> + where CS: ConstraintSystem + { + let bit_values = match self.value { + Some(value) => { + let mut field_char = BitIterator::new(E::Fr::char()); + + let mut tmp = Vec::with_capacity(E::Fr::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(), E::Fr::NUM_BITS as usize); + + tmp + }, + None => { + vec![None; E::Fr::NUM_BITS as usize] + } + }; + + let mut bits = vec![]; + for (i, b) in bit_values.into_iter().enumerate() { + bits.push(AllocatedBit::alloc( + cs.namespace(|| format!("bit {}", i)), + b + )?); + } + + let mut lc = LinearCombination::zero(); + let mut coeff = E::Fr::one(); + + for bit in bits.iter().rev() { + lc = lc + (coeff, bit.get_variable()); + + coeff.double(); + } + + lc = lc - self.variable; + + cs.enforce( + || "unpacking constraint", + LinearCombination::zero(), + LinearCombination::zero(), + lc + ); + + Ok(bits.into_iter().map(|b| Boolean::from(b)).collect()) + } + pub fn from_bits_strict( mut cs: CS, bits: &[Boolean] @@ -239,6 +302,46 @@ mod test { } } + #[test] + fn test_into_bits() { + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let r = Fr::rand(&mut rng); + let mut cs = TestConstraintSystem::::new(); + + let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); + + let bits = n.into_bits(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + + for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter()) { + if let &Boolean::Is(ref a) = a { + assert_eq!(b, a.get_value().unwrap()); + } else { + unreachable!() + } + } + + cs.set("num", Fr::rand(&mut rng)); + assert!(!cs.is_satisfied()); + cs.set("num", r); + assert!(cs.is_satisfied()); + + for i in 0..Fr::NUM_BITS { + let name = format!("bit {}/boolean", i); + let cur = cs.get(&name); + let mut tmp = Fr::one(); + tmp.sub_assign(&cur); + cs.set(&name, tmp); + assert!(!cs.is_satisfied()); + cs.set(&name, cur); + assert!(cs.is_satisfied()); + } + } + } + #[test] fn test_from_bits_strict() { {