Construct `FpVar` from bits (#35)
Pratyush Mishra pratyushmishra@berkeley.edu
Mon, 11 Jan 2021 16:24:39 -0800
1 files changed,
84 insertions(+),
1 deletions(-)
jump to
M
src/bits/boolean.rs
→
src/bits/boolean.rs
@@ -1,4 +1,4 @@
-use ark_ff::{BitIteratorBE, Field, PrimeField}; +use ark_ff::{BitIteratorBE, Field, FpParameters, PrimeField}; use crate::{fields::fp::FpVar, prelude::*, Assignment, ToConstraintFieldGadget, Vec}; use ark_relations::r1cs::{@@ -605,6 +605,56 @@ Is(_) | Not(_) => {
r.cs() .enforce_constraint(r.lc(), lc!() + Variable::One, lc!() + Variable::One) } + } + } + + /// Convert a little-endian bitwise representation of a field element to `FpVar<F>` + #[tracing::instrument(target = "r1cs", skip(bits))] + pub fn le_bits_to_fp_var(bits: &[Self]) -> Result<FpVar<F>, SynthesisError> + where + F: PrimeField, + { + // Compute the value of the `FpVar` variable via double-and-add. + let mut value = None; + let cs = bits.cs(); + // Assign a value only when `cs` is in setup mode, or if we are constructing + // a constant. + let should_construct_value = (!cs.is_in_setup_mode()) || bits.is_constant(); + if should_construct_value { + let bits = bits.iter().map(|b| b.value().unwrap()).collect::<Vec<_>>(); + let bytes = bits + .chunks(8) + .map(|c| { + let mut value = 0u8; + for (i, &bit) in c.iter().enumerate() { + value += (bit as u8) << i; + } + value + }) + .collect::<Vec<_>>(); + value = Some(F::from_le_bytes_mod_order(&bytes)); + } + + if bits.is_constant() { + Ok(FpVar::constant(value.unwrap())) + } else { + let mut power = F::one(); + // Compute a linear combination for the new field variable, again + // via double and add. + let mut combined_lc = LinearCombination::zero(); + bits.iter().for_each(|b| { + combined_lc = &combined_lc + (power, b.lc()); + power.double_in_place(); + }); + // Allocate the new variable as a SymbolicLc + let variable = cs.new_lc(combined_lc)?; + // If the number of bits is less than the size of the field, + // then we do not need to enforce that the element is less than + // the modulus. + if bits.len() >= F::Params::MODULUS_BITS as usize { + Self::enforce_in_field_le(bits)?; + } + Ok(crate::fields::fp::AllocatedFp::new(value, variable, cs.clone()).into()) } }@@ -1735,6 +1785,39 @@ assert_eq!(r.value()?, expected);
} } } + Ok(()) + } + + #[test] + fn test_bits_to_fp() -> Result<(), SynthesisError> { + use AllocationMode::*; + let rng = &mut ark_std::test_rng(); + let cs = ConstraintSystem::<Fr>::new_ref(); + + let modes = [Input, Witness, Constant]; + for &mode in modes.iter() { + for _ in 0..1000 { + let f = Fr::rand(rng); + let bits = BitIteratorLE::new(f.into_repr()).collect::<Vec<_>>(); + let bits: Vec<_> = + AllocVar::new_variable(cs.clone(), || Ok(bits.as_slice()), mode)?; + let f = AllocVar::new_variable(cs.clone(), || Ok(f), mode)?; + let claimed_f = Boolean::le_bits_to_fp_var(&bits)?; + claimed_f.enforce_equal(&f)?; + } + + for _ in 0..1000 { + let f = Fr::from(u64::rand(rng)); + let bits = BitIteratorLE::new(f.into_repr()).collect::<Vec<_>>(); + let bits: Vec<_> = + AllocVar::new_variable(cs.clone(), || Ok(bits.as_slice()), mode)?; + let f = AllocVar::new_variable(cs.clone(), || Ok(f), mode)?; + let claimed_f = Boolean::le_bits_to_fp_var(&bits)?; + claimed_f.enforce_equal(&f)?; + } + assert!(cs.is_satisfied().unwrap()); + } + Ok(()) } }