mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-10 16:01:28 +01:00
Construct FpVar from bits (#35)
This commit is contained in:
@@ -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 crate::{fields::fp::FpVar, prelude::*, Assignment, ToConstraintFieldGadget, Vec};
|
||||||
use ark_relations::r1cs::{
|
use ark_relations::r1cs::{
|
||||||
@@ -608,6 +608,56 @@ impl<F: Field> Boolean<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Enforces that `bits`, when interpreted as a integer, is less than
|
/// Enforces that `bits`, when interpreted as a integer, is less than
|
||||||
/// `F::characteristic()`, That is, interpret bits as a little-endian
|
/// `F::characteristic()`, That is, interpret bits as a little-endian
|
||||||
/// integer, and enforce that this integer is "in the field Z_p", where
|
/// integer, and enforce that this integer is "in the field Z_p", where
|
||||||
@@ -1737,4 +1787,37 @@ mod test {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user